Frage

Während Code Refactoring und befreit mich von all jenem #defines, die wir jetzt hassen gelehrt, ich kam in dieser Schönheit verwendet, um die Anzahl der Elemente in einer Struktur zu berechnen:

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

Sehr nützlich, wie es ist, aber es kann in eine Inline-Funktion oder eine Vorlage umgewandelt werden?

OK, würde ARRAYSIZE ein besserer Name, aber das ist Legacy-Code (keine Ahnung, woher es kam, es ist mindestens 15 Jahre alt), damit ich klebte es ‚wie besehen‘.

War es hilfreich?

Lösung

Wie bereits erwähnt worden ist, arbeitet der Code tatsächlich die Anzahl der Elemente in einem Array aus, nicht struct. Ich würde nur explizit die sizeof () Division schreiben, wenn ich es will. Wenn ich es, eine Funktion zu machen wäre, würde ich wollen, um es in seiner Definition deutlich machen, dass es ein Array ist zu erwarten.

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

Das oben gezeigte ähnelt xtofl der , (das sagt Punkt zu einem dynamisch zugewiesenen Array) und bekommen die falsche Antwort versehentlich außer es schützt gegen einen Zeiger auf sie übergeben.

Bearbeiten : Vereinfachtes per JohnMcG . Bearbeiten :. Inline

Leider ist die oben stellt keine Kompilierung Antwort (auch wenn die Compiler Inline-Design & optimiert es eine Konstante unter der Haube sein), also nicht als die Kompilierung konstanter Ausdruck verwendet werden kann. das heißt, sie kann nicht als eine Größe verwendet werden, um ein statisches Array zu erklären. Unter C ++ 0x, geht dieses Problem weg, wenn man das Schlüsselwort ersetzt inline von constexpr (constexpr ist inline implizit).

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

jwfearn der Lösung Arbeit für Kompilierung, sondern beinhaltet eine typedef, die effektiv die Größe des Arrays in der Deklaration einen neuen Namen „gerettet“. Die Größe des Arrays wird arbeitete dann aus, indem eine Konstante über diesen neuen Namen initialisiert. In einem solchen Fall kann man auch einfach speichern Sie die Array-Größe in eine Konstante von Anfang an.

Martin York geschrieben Lösung auch unter Arbeit Kompilierung, sondern beinhaltet den nicht-Standard- typeof mit () Operator. Die Arbeit herum, dass entweder warten, C ++ 0x und verwenden decltype (durch die Zeit würde man nicht wirklich es braucht für dieses Problem, da wir haben constexpr ). Eine weitere Alternative ist Boost.Typeof zu verwenden, wobei in diesem Fall werden wir am Ende mit

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

und wird durch Schreiben

verwendet
ArraySize<BOOST_TYPEOF(foo)>::size

Dabei steht foo ist der Name eines Arrays.

Andere Tipps

Keiner von ihnen hat bisher einen tragbaren Weg vorgeschlagen, um die Größe eines Arrays zu erhalten, wenn Sie nur eine Instanz eines Arrays haben und nicht seine Art. (Typeof und _countof nicht tragbar ist, so kann nicht verwendet werden.)

Ich würde es tun die folgende Art und Weise:

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
}
  • Es funktioniert, wenn nur der Wert um.
  • Es ist tragbar und verwendet nur STD-C ++.
  • Es schlägt mit einer descriptiv Fehlermeldung aus.
  • Sie nicht den Wert bewerten. (Ich kann nicht aus einer Situation denken, wo dies ein Problem sein würde, weil Array-Typ nicht durch eine Funktion zurückgeführt werden kann, aber besser sicher als traurig.)
  • Es gibt die Größe als compiletime konstant.

Ich wickelte das Konstrukt in einen Makro ein paar anständige Syntax. Wenn Sie es loswerden wollen Ihre einzige Option ist manuell um die Substitution zu tun.

KTC 's Lösung ist sauber, aber es kann nicht zur Compile-Zeit verwendet werden, und es ist abhängig von Compiler Optimierung Code-aufblähen und Funktionsaufruf Overhead zu vermeiden.

Man kann Array-Größe mit einem Compile-Zeit-nur metafunction mit Null-Laufzeitkosten berechnen. BCS war auf dem richtigen Weg, aber diese Lösung ist falsch.

Hier ist meine Lösung:

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

mit Testcode (mit Boost.StaticAssert Compile-Zeit-only Verwendung zu demonstrieren):

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

Diese Lösung Nicht-Array-Typen bei der Kompilierung ablehnt, damit es nicht durch Zeiger als die Makro-Version tut verwechselt wird erhalten.

Das Makro hat eine sehr irreführende Bezeichnung -. Der Ausdruck im Makro die Anzahl der Elemente in einem Array zurück, wenn der Name eines Arrays wird als Makro Parameter übergibt

Für andere Arten Sie werden etwas mehr oder weniger bedeutungslos, wenn der Typ ein Zeiger ist oder Sie einen Syntaxfehler erhalten.

In der Regel, dass Makro heißt so etwas wie num_elements () oder etwas seine wahre Nützlichkeit anzuzeigen. Es ist nicht möglich, das Makro mit einer Funktion in C, aber in C ++ ersetzen eine Vorlage verwendet werden kann.

Die Version, die ich verwenden ist, basierend auf Code in Microsofts winnt.h Header (lassen Sie es mich wissen, wenn diese Schnipsel Posting über Fair Use geht):

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

Auch Matthew Wilson Buch "Imperfect C ++" hat eine schöne Behandlung, was los hier (Abschnitt 14.3 - Seite 211-213 - Arrays und Zeiger - dimensionof ()).

Ihr Makro falsch benannt wird, sollte es ARRAYSIZE aufgerufen werden. Es wird verwendet, um die Anzahl der Elemente in einem Array bestimmen whos Größe zur Kompilierzeit fixiert ist. Hier ist ein Weg, es funktionieren kann:

  

char foo [128]; In Wirklichkeit //, dann würden Sie   haben eine gewisse konstante oder konstant   Ausdruck als Array-Größe.

     

für (unsigned i = 0; i

Es ist eine Art spröde zu verwenden, da Sie diesen Fehler machen:

  

char * foo = new char [128];

     

für (unsigned i = 0; i

Sie werden nun für i = 0 bis <1 iterieren und reißen die Haare aus.

Der Typ einer Template-Funktion wird automatisch im Gegensatz zu derjenigen einer Template-Klasse abgeleitet. Sie können es sogar noch einfacher:

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


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

Aber ich es tun stimmen für structs nicht funktioniert: Es ist für Arrays funktioniert. So würde ich lieber nennen es Arraysize:)

Simplfying @ KTC sind, da wir die Größe des Arrays in dem Template-Argumente haben:

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

Der Nachteil ist, dass Sie eine Kopie davon in die Binärdatei für jede Type-Name, Größe Kombination haben.

  • Funktion, keine Template-Funktion, ja
  • template, ich glaube, so (aber C ++
  • Vorlagen sind nicht mein Ding)

Edit: Von Dougs Code

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

Ich habe gesagt, dass die zweite ein nicht funktioniert. OTOH so etwas wie es praktikabel sein sollte, verwende ich C einfach nicht ++ genug, um es zu beheben.

einer Seite auf C ++ (und D) Vorlagen für die Kompilierung Sachen

Ich ziehe die Enum-Methode vorgeschlagen durch [BCS] (in Kann man das Makro auf eine Funktion umgewandelt werden? )

Das ist, weil Sie können es verwenden, wo der Compiler eine Kompilierung konstant erwartet. Die aktuelle Version der Sprache können Sie verwenden keine Funktionen Ergebnisse für Kompilierung consts aber ich glaube, dass dies in der nächsten Version des Compilers kommen:

Das Problem bei dieser Methode ist, dass es keine Kompilierung Fehler, wenn sie mit einer Klasse verwendet generiert, der den ‚*‘ Operator (siehe Code unten) überlastet ist.

Leider kompilieren lässt die Version von ‚BCS‘ geliefert nicht ganz so so hier erwartet ist meine 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];
}

Ich glaube nicht, dass das wirklich die Anzahl der Elemente in einer Struktur nicht klappt. Wenn die Struktur verpackt und verwendet man die Dinge kleiner als die Zeigergröße (wie char auf einem 32-Bit-System), dann sind Ihre Ergebnisse falsch. Auch wenn die Struktur eine Struktur enthält, die Sie sind auch falsch!

Ja, es kann eine Vorlage in C ++ gemacht werden

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

zu verwenden:

struct JibbaJabba
{
   int int1;
   float f;
};

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

Siehe BCS den Beitrag oben oder unten über eine coole Art, dies mit einer Klasse bei der Kompilierung mit etwas Licht Metaprogrammierung zu tun.

xtofl hat die richtige Antwort für die Suche nach einer Array-Größe. Kein Makro oder eine Vorlage sollte für die Suche nach der Größe einer Struktur, da sizeof () notwendig sein sollte gut tun.

Ich stimme der Präprozessor ist böse , aber es gibt Gelegenheiten, bei denen es die kleinste Übel der Alternativer .

Wie JohnMcG Antwort, aber

Nachteil ist, dass Sie eine Kopie davon in die Binärdatei für jede Type-Name, Größe Kombination haben.

Das ist, warum Sie es machen würden eine Inline Template-Funktion.

Windows-spezifisch:

Dort wird das Makro _countof() durch die CRT genau für diesen Zweck zugeführt wird.

Ein Link auf das Dokument bei MSDN

Für C99-Stil Arrays variabler Länge, scheint es, dass die reine Makro-Ansatz (sizeof (arr) / sizeof (arr [0])) ist die einzige, die funktioniert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top