Question

Tout en refacturant le code et en me débarrassant de toutes ces # définitions que l'on nous apprend maintenant à détester, je suis tombé sur cette beauté utilisée pour calculer le nombre d'éléments dans une structure:

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

Très utile tel quel, mais peut-il être converti en une fonction ou un modèle inline?

OK, ARRAYSIZE serait un meilleur nom, mais c’est un code ancien (je ne sais pas d’où il vient, il a au moins 15 ans), donc je l’ai collé "tel quel".

Était-ce utile?

La solution

Comme indiqué précédemment, le code détermine le nombre d'éléments d'un tableau, et non de struct. Je voudrais juste écrire la division sizeof () explicitement quand je le veux. Si je devais en faire une fonction, je voudrais préciser dans sa définition qu’elle attend un tableau.

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

Ce qui précède est similaire à de xtofl , sauf qu'il évite de lui passer un pointeur (qui dit pointer vers un tableau alloué dynamiquement) et d'obtenir la mauvaise réponse par erreur.

MODIFIER : simplifié selon JohnMcG . MODIFIER : en ligne.

Malheureusement, ce qui précède ne fournit pas de réponse à la compilation (même si le compilateur & est intégré; optimisez-le pour qu'il soit une constante), ne pouvez donc pas être utilisé comme expression de constante de compilation. c'est-à-dire qu'il ne peut pas être utilisé comme taille pour déclarer un tableau statique. Sous C ++ 0x, ce problème disparaît si l'on remplace le mot clé inline par constexpr (constexpr est implicitement en ligne).

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

La solution de jwfearn fonctionne pour le temps de compilation, mais impliquent d'avoir une typedef qui & "; effectivement" sauvé & "; la taille du tableau dans la déclaration d'un nouveau nom. La taille du tableau est ensuite calculée en initialisant une constante via ce nouveau nom. Dans ce cas, vous pouvez également simplement enregistrer la taille du tableau dans une constante dès le début.

La

solution publiée de Martin York fonctionne également sous compile, mais implique l’utilisation de l’opérateur non-standard typeof () . Pour contourner ce problème, attendez C ++ 0x et utilisez decltype (à ce moment-là, vous n'en aurez plus besoin pour ce problème car nous aurons constexpr . ). Une autre alternative consiste à utiliser Boost.Typeof, auquel cas nous nous retrouverons avec

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

et est utilisé en écrivant

ArraySize<BOOST_TYPEOF(foo)>::size

toto est le nom d'un tableau.

Autres conseils

Aucun n’a jusqu’à présent proposé un moyen portable d’obtenir la taille d’un tableau lorsque vous n’avez qu’une instance d’un tableau et pas son type. (typeof et _countof ne sont pas portables, donc ne peuvent pas être utilisés.)

Je le ferais de la manière suivante:

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
}
  • Cela fonctionne lorsque seule la valeur est présente.
  • Il est portable et utilise uniquement std-C ++.
  • Il échoue avec un message d'erreur descriptif.
  • Cela n'évalue pas la valeur. (Je ne peux pas imaginer une situation dans laquelle cela poserait un problème, car le type de tableau ne peut pas être renvoyé par une fonction, mais il vaut mieux prévenir que guérir.)
  • Il renvoie la taille sous forme de constante compilétime.

J'ai intégré la construction dans une macro pour obtenir une syntaxe convenable. Si vous souhaitez vous en débarrasser, votre seule option est de procéder à la substitution manuellement.

La solution de KTC est propre, mais elle ne peut pas être utilisée à la compilation et dépend du compilateur. optimisation pour éviter le surcoût lié à l'appel de code et de fonction.

On peut calculer la taille d'un tableau avec une métafonction au moment de la compilation uniquement avec un coût d'exécution nul. BCS était sur la bonne voie, mais cette solution est incorrecte.

Voici ma solution:

// 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 ) 

avec le code de test (avec Boost.StaticAssert pour démontrer l’utilisation uniquement au moment de la compilation):

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

Cette solution rejette les types non-array au moment de la compilation afin d'éviter toute confusion avec les pointeurs, contrairement à la version macro.

La macro a un nom très trompeur - l'expression dans la macro retournera le nombre d'éléments dans un tableau si le nom d'un tableau est passé en tant que paramètre de macro.

Pour les autres types, vous obtiendrez quelque chose de plus ou de moins de sens si le type est un pointeur ou si vous obtenez une erreur de syntaxe.

Habituellement, cette macro s'appelle NUM_ELEMENTS () ou quelque chose qui indique sa véritable utilité. Il n'est pas possible de remplacer la macro par une fonction en C, mais un modèle peut être utilisé en C ++.

La version que j'utilise est basée sur le code figurant dans l'en-tête winnt.h de Microsoft (veuillez me faire savoir si la publication de cet extrait de code dépasse le cadre de l'utilisation équitable):

//
// 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)

De plus, le livre de Matthew Wilson & "; Imperfect C ++ &"; traite bien de ce qui se passe ici (Section 14.3 - pages 211-213 - Tableaux et pointeurs - dimensionof ()).

Votre macro est mal nommée, elle devrait s'appeler ARRAYSIZE. Il est utilisé pour déterminer le nombre d'éléments d'un tableau dont la taille est fixée lors de la compilation. Voici une façon dont cela peut fonctionner:

  

char foo [128]; // En réalité, vous auriez   avoir une constante ou constante   expression comme la taille du tableau.

     

pour (unsigned i = 0; i < STRUCTSIZE (   foo); ++ i) {}

C'est un peu fragile à utiliser, car vous pouvez commettre cette erreur:

  

char * foo = new char [128];

     

pour (unsigned i = 0; i < STRUCTSIZE (   foo); ++ i) {}

Vous allez maintenant itérer de i = 0 à < 1 et arrache tes cheveux.

Le type d'une fonction de modèle est déduit automatiquement, contrairement à celui d'une classe de modèle. Vous pouvez l'utiliser encore plus simplement:

template< typename T > size_t structsize( const T& t ) { 
  return sizeof( t ) / sizeof( *t ); 
}


int ints[] = { 1,2,3 };
assert( structsize( ints ) == 3 );

Mais je conviens que cela ne fonctionne pas pour les structures: cela fonctionne pour les tableaux. Je préférerais donc l'appeler Arraysize:)

Simplifier @ KTC, puisque nous avons la taille du tableau dans l'argument de modèle:

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

L’inconvénient est que vous aurez une copie de cela dans votre fichier binaire pour chaque combinaison Nom de type, taille.

  • fonction, pas de fonction modèle, oui
  • modèle, je pense que oui (mais C ++
  • les modèles ne sont pas mon truc)

Modifier: à partir du code 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) };
}

On m'a dit que le deuxième ne fonctionnait pas. OTOH quelque chose comme ça devrait être réalisable, je n’utilise pas assez C ++ pour le réparer.

Une page sur les modèles C ++ (et D) pour les tâches de compilation

Je préfère la méthode enum proposée par [BCS] (dans Cette macro peut-elle être convertie en une fonction? )

C’est parce que vous pouvez l’utiliser là où le compilateur attend une constante de temps de compilation. La version actuelle du langage ne vous permet pas d'utiliser les résultats des fonctions pour les consts de compilation, mais je crois que cela va venir dans la prochaine version du compilateur:

Le problème avec cette méthode est qu’elle ne génère pas d’erreur de compilation lorsqu’elle est utilisée avec une classe qui a surchargé l’opérateur '*' (voir le code ci-dessous pour plus de détails).

Malheureusement, la version fournie par 'BCS' ne compile pas comme prévu, voici donc ma version:

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

Je ne pense pas que cela détermine vraiment le nombre d'éléments d'une structure. Si la structure est compactée et que vous avez utilisé des éléments plus petits que la taille du pointeur (tels que les caractères sur un système 32 bits), vos résultats sont erronés. De plus, si la structure contient une structure, vous vous trompez également!

Oui, vous pouvez créer un modèle en C ++

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

à utiliser:

struct JibbaJabba
{
   int int1;
   float f;
};

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

Consultez l'article de BCS ci-dessus ou ci-dessous sur une méthode intéressante pour le faire avec une classe au moment de la compilation, en utilisant une métaprogrammation de modèles légers.

xtofl a la bonne réponse pour trouver une taille de tableau. Aucune macro ou modèle ne devrait être nécessaire pour trouver la taille d'une structure, puisque sizeof () devrait bien le faire.

Je suis d'accord avec le préprocesseur, c'est mal , mais il y a des cas où il s'agit du moindre mal de la alternatives .

Comme la réponse de JohnMcG, mais

L’inconvénient est que vous aurez une copie de cela dans votre fichier binaire pour chaque combinaison Nom de type, taille.

C’est pourquoi vous en feriez une fonction de modèle intégrée .

Spécifique à Windows:

Il existe la macro _countof() fournie par le CRT exactement à cette fin.

Un lien vers la documentation sur MSDN

Pour les tableaux de longueur variable de style C99, il semble que la seule approche macro (sizeof (arr) / sizeof (arr [0])) soit la seule qui fonctionne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top