Pergunta

Durante a refatoração de código e livrando-me de todas aquelas # define que agora estamos ensinados a odiar, me deparei com esta beleza usado para calcular o número de elementos em uma estrutura:

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

Muito útil como ele é, mas ele pode ser convertido em uma função inline ou modelo?

OK, ArraySize seria um nome melhor, mas este é o código legado (nenhuma idéia de onde ele veio, que é pelo menos 15 anos de idade), então eu colei 'como está'.

Foi útil?

Solução

Como foi dito, o código realmente trabalhar para fora o número de elementos em uma matriz, não estrutura. Gostaria apenas de escrever a divisão sizeof () explicitamente quando eu quiser. Se eu fosse para torná-lo uma função, eu gostaria de deixar claro na sua definição que está esperando uma matriz.

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

A descrição acima é semelhante ao de xtofl , Só que protege contra passando um ponteiro para ele (que diz que apontam para uma matriz alocada dinamicamente) e obter a resposta errada por engano.

Editar : simplificado conforme JohnMcG . Editar :. Em linha

Infelizmente, o acima não fornece uma resposta tempo de compilação (até mesmo se o compilador faz incorporada & optimizá-lo para ser uma constante sob a capa), de modo que não pode ser usado como uma expressão constante de tempo de compilação. isto é, não pode ser usado como tamanho de declarar uma matriz estática. Sob C ++ 0x, este problema desaparecer se substituirmos a palavra-chave em linha por constexpr (constexpr está em linha implicitamente).

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

de jwfearn solução trabalho para tempo de compilação, mas envolvem ter um typedef que efetivamente "salvou" o tamanho da matriz na declaração de um novo nome. O tamanho da matriz é então trabalhado por inicializar uma constante através desse novo nome. Em tal caso, pode-se assim simplesmente excepto o tamanho da matriz em uma constante desde o início.

do Martin Iorque postou solução também trabalho sob tempo de compilação, mas envolvem usando o não-padrão typeof () operador. O trabalho em torno para que seja espera para C ++ 0x e uso decltype (pelo qual uma vez que na verdade não precisa dele para este problema, como teremos constexpr ). Outra alternativa é usar Boost.Typeof, caso em que vamos acabar com

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

e é usado por escrito

ArraySize<BOOST_TYPEOF(foo)>::size

que foo é o nome de uma matriz.

Outras dicas

Nenhum até agora propôs uma maneira portátil para obter o tamanho de uma matriz quando você só tem uma instância de uma matriz e não seu tipo. (Typeof e _countof não é tão portátil não pode ser usado.)

Eu faria isso da seguinte forma:

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
}
  • Ele funciona quando apenas o valor é de cerca de.
  • É portátil e apenas usos STD-C ++.
  • É falha com uma mensagem de erro descriptiv.
  • Ele não avalia o valor. (Eu não posso pensar de uma situação em que isso seria um problema porque o tipo de matriz não pode ser retornado por uma função, mas é melhor prevenir do que remediar.)
  • Ele retorna o tamanho como constante da compilação.

Eu passei a construção em uma macro para ter alguma sintaxe decente. Se você quiser se livrar dele sua única opção é fazer a substituição manualmente.

KTC 's solução é limpo, mas não pode ser usado em tempo de compilação e é dependente do compilador otimização para evitar que o código-inchaço e sobrecarga chamada de função.

Pode-se calcular o tamanho da matriz com um metafunção compilação só de tempo com custo de tempo de execução zero. BCS estava no caminho certo, mas que a solução está incorreto.

Aqui está a minha solução:

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

com código de teste (usando Boost.StaticAssert demonstrar-tempo de compilação único uso):

// 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 solução rejeita tipos não-matriz em tempo de compilação para que não se confunda com ponteiros como a versão macro faz.

A macro tem um nome muito enganosa -. A expressão na macro irá retornar o número de elementos em uma matriz se o nome de um array é passado como o parâmetro macro

Para outros tipos você terá algo mais ou menos sem sentido se o tipo é um ponteiro ou você vai ter um erro de sintaxe.

Normalmente, essa macro é nomeado algo como NUM_ELEMENTS () ou algo para indicar a sua verdadeira utilidade. Não é possível substituir a macro com uma função em C, mas em C ++ um modelo pode ser usado.

O uso versão que é baseado no código no cabeçalho winnt.h da Microsoft (por favor deixe-me saber se publicar esse trecho vai além do uso justo):

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

Além disso, o livro de Matthew Wilson "Imperfect C ++" tem um tratamento agradável do que está acontecendo aqui (Seção 14.3 - página 211-213 - Matrizes e ponteiros - dimensionof ()).

Seu macro é misnamed, ele deve ser chamado ArraySize. Ele é usado para determinar o número de elementos em uma matriz whos tamanho é fixado no tempo de compilação. Aqui está uma maneira que pode funcionar:

de char foo [128]; // Na realidade, você tem alguma constante ou constante expressão como o tamanho da matriz.

for (unsigned i = 0; i

É uma espécie de frágil para uso, porque você pode cometer este erro:

char * foo = new char [128];

for (unsigned i = 0; i

Agora vai iterate para i = 0 a <1 e arrancar os cabelos.

O tipo de uma função de modelo é inferida automaticamente, em contraste com a de uma classe de modelo. Você pode usá-lo ainda mais simples:

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


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

Mas eu concordo que não funciona para estruturas: ele funciona para matrizes. Então, eu prefiro chamá-lo ArraySize:)

Simplfying @ KTC de, uma vez que temos o tamanho da matriz no argumento de modelo:

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

A desvantagem é que você terá uma cópia deste no seu binário para cada TypeName, combinação Size.

  • função, nenhuma função de modelo, sim
  • modelo, eu acho que sim (mas C ++
  • modelos não são minha coisa)

Editar: a partir do 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) };
}

Eu tenho dito que o segundo não funciona. OTOH algo como isso deve ser viável, eu só não uso C ++ suficiente para corrigi-lo.

Uma página em C ++ (e D) modelos para coisas tempo de compilação

Eu prefiro o método enum sugerido por [BCS] (em pode esta macro ser convertido para uma função? )

Isso é porque você pode usá-lo onde o compilador está esperando uma constante de tempo de compilação. A versão atual da linguagem não permite que você use funções resultados para consts tempo de compilação mas acredito que esta por vir na próxima versão do compilador:

O problema com este método é que ele não gera um erro de tempo de compilação quando usado com uma classe que tem sobrecarregado o operador '*' (ver código abaixo para detalhes).

Infelizmente a versão fornecida por 'BCS' não chega a compilar como esperado por isso aqui é a minha versão:

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

Eu não acho que isso realmente funciona para fora o número de elementos em uma estrutura. Se a estrutura é embalado e você usou coisas menores do que o tamanho do ponteiro (como carvão em um sistema 32-bit), em seguida, seus resultados estão errados. Além disso, se a estrutura contém um struct você está errado também!

Sim, pode ser feito um modelo em C ++

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

Para usar:

struct JibbaJabba
{
   int int1;
   float f;
};

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

pós See de BCS acima ou abaixo sobre uma forma legal de fazer isso com uma classe em tempo de compilação usando algum modelo metaprogramming luz.

xtofl tem a resposta certa para encontrar um tamanho de matriz. No macro ou modelo deve ser necessário para encontrar o tamanho de uma struct, já que sizeof () deve fazer muito bem.

Eu concordo pré-processador é mau , mas há ocasiões em que é o menos mal do alternativas .

A resposta de como JohnMcG, mas

Desvantagem é que você terá uma cópia deste no seu binário para cada TypeName, Tamanho combinação.

É por isso que você tinha que fazer isso um em linha função de modelo.

Windows específica:

Existe a _countof() macro fornecido pela CRT exatamente para esta finalidade.

Um link para o doc no MSDN

Para C99-style matrizes de comprimento variável, parece que a abordagem macro puro (sizeof (arr) / sizeof (arr [0])) é o único que vai funcionar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top