Esta macro pode ser convertido para uma função?
-
01-07-2019 - |
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á'.
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.
#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],©[0]);
std::copy(©[0],©[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.
respondidas em detalhes aqui: determinação Array Size Parte 1 e aqui: matriz determinação Tamanho Parte 2 .
Windows específica:
Existe a _countof()
macro fornecido pela CRT exatamente para esta finalidade.
Para C99-style matrizes de comprimento variável, parece que a abordagem macro puro (sizeof (arr) / sizeof (arr [0])) é o único que vai funcionar.