Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how can I compute size of static C table in compile time ?

162 views
Skip to first unread message

Bronek Kozicki

unread,
Jan 2, 2004, 11:11:28 PM1/2/04
to
Probably the most popular solution of problem stated in topic is
compile-time expresion:
sizeof(table)/sizeof(table[0])

However, I do not like so much typing. I know I can use macro, but
macros are (at least in my project) not welcome. I'm searching for safer
solution, possibly using templates.

If I need to know size in runtime, I could use following:
template <typename T, size_t S>
int table_size(T (&t) [S]) {return S;}

however, I need to know this size in compile time. Any ideas ?


B.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Graeme Prentice

unread,
Jan 3, 2004, 10:02:00 AM1/3/04
to
On 2 Jan 2004 23:11:28 -0500, Bronek Kozicki wrote:

>Probably the most popular solution of problem stated in topic is
>compile-time expresion:
>sizeof(table)/sizeof(table[0])
>
>However, I do not like so much typing. I know I can use macro, but
>macros are (at least in my project) not welcome. I'm searching for safer
>solution, possibly using templates.
>
>If I need to know size in runtime, I could use following:
>template <typename T, size_t S>
>int table_size(T (&t) [S]) {return S;}
>
>however, I need to know this size in compile time. Any ideas ?


#include <iostream>

template <size_t x>
struct helper
{
typedef char ar[x];
ar a1;
};

template <typename T, size_t S>

typename helper<S>::ar& table_size(T (&t) [S])
{
return helper<S>().a1;
// return S; // ok with GCC / EDG because no instantiation
}

int main()
{
int x1[67];
static const int z = sizeof( table_size(x1) );
std::cout << z;
}


We use a macro called ELEMENTS_OF and insist on all uppercase names
being reserved for macros - some people have trouble following this
rule though ...

Graeme

Jonathan Turkanis

unread,
Jan 3, 2004, 11:37:24 AM1/3/04
to
"Bronek Kozicki" <br...@rubikon.pl> wrote in message
news:169gadiucxo8$.2gy1il2k64sb.dlg@40tude.net...

> Probably the most popular solution of problem stated in topic is
> compile-time expresion:
> sizeof(table)/sizeof(table[0])
>
> However, I do not like so much typing. I know I can use macro, but
> macros are (at least in my project) not welcome. I'm searching for safer
> solution, possibly using templates.
>
> If I need to know size in runtime, I could use following:
> template <typename T, size_t S>
> int table_size(T (&t) [S]) {return S;}
>
> however, I need to know this size in compile time. Any ideas ?
>

You can use the following code, adapted from Thorsten Ottosen's container
traits library in the boost sandbox (see
http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/)

namespace detail
{
template< std::size_t sz >
class array_size
{
char give_size[sz];
};
}

template< typename T, std::size_t sz >
detail::array_size<sz>
sizer( const T (&array)[sz] );

template< typename T, std::size_t sz >
detail::array_size<sz>
sizer( T (&array)[sz] );

Usage: sizeof(sizer(t)).

HTH.

Jonathan

Ralf

unread,
Jan 3, 2004, 11:38:04 AM1/3/04
to
"Bronek Kozicki" <br...@rubikon.pl> schrieb im Newsbeitrag
news:169gadiucxo8$.2gy1il2k64sb.dlg@40tude.net...

> Probably the most popular solution of problem stated in topic is
> compile-time expresion:
> sizeof(table)/sizeof(table[0])
>
> However, I do not like so much typing. I know I can use macro, but
> macros are (at least in my project) not welcome. I'm searching for safer
> solution, possibly using templates.
>
> If I need to know size in runtime, I could use following:
> template <typename T, size_t S>
> int table_size(T (&t) [S]) {return S;}
>
> however, I need to know this size in compile time. Any ideas ?

I think, you can't get it out in compile time, because you want it from an
array name (constant pointer) and not from a type.
If you want it from a type, you will take the struct to get it. From a array
name you need a function, wich has the runtime aspect. Indeed, you can make
the function inline. The compiler will optimize it and calculate the value
at compile time.
But you can't use it inside of preprocessor directives.

Ralf

www.oop-trainer.de/
www.cplusplus-kurse.de/ooptrainer/

Pete Becker

unread,
Jan 3, 2004, 9:22:45 PM1/3/04
to
Jonathan Turkanis wrote:
>
> You can use the following code, adapted from Thorsten Ottosen's container
> traits library in the boost sandbox (see
> http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/)
>
> namespace detail
> {
> template< std::size_t sz >
> class array_size
> {
> char give_size[sz];
> };
> }
>
> template< typename T, std::size_t sz >
> detail::array_size<sz>
> sizer( const T (&array)[sz] );
>
> template< typename T, std::size_t sz >
> detail::array_size<sz>
> sizer( T (&array)[sz] );
>
> Usage: sizeof(sizer(t)).
>

But that uses a macro. <g> More to the point, it's an awful lot of code
to write for very little benefit.

#define ARR_SIZE(x) (sizeof(x)/sizeof(x[0]))

Okay, sure, if you use this with a pointer instead of an array you'll
get the wrong answer. Hypothetically, that is. I've never had that
problem. Don't waste time writing clever templates when a macro does the
job more clearly and with less code.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

Pete Becker

unread,
Jan 3, 2004, 9:23:28 PM1/3/04
to
Bronek Kozicki wrote:
>
> Probably the most popular solution of problem stated in topic is
> compile-time expresion:
> sizeof(table)/sizeof(table[0])
>
> However, I do not like so much typing. I know I can use macro, but
> macros are (at least in my project) not welcome. I'm searching for safer
> solution, possibly using templates.
>

But your original solution wouldn't be welcome, either, since it uses
the macro sizeof. <g> Working around arbitrary constraints can be a
useful educational experience, but real-world programmers do use macros.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Graeme Prentice

unread,
Jan 3, 2004, 9:24:12 PM1/3/04
to
On 3 Jan 2004 11:37:24 -0500, Jonathan Turkanis wrote:

>
>You can use the following code, adapted from Thorsten Ottosen's container
>traits library in the boost sandbox (see
>http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/)
>
> namespace detail
> {
> template< std::size_t sz >
> class array_size
> {
> char give_size[sz];
> };
> }
>
> template< typename T, std::size_t sz >
> detail::array_size<sz>
> sizer( const T (&array)[sz] );
>
> template< typename T, std::size_t sz >
> detail::array_size<sz>
> sizer( T (&array)[sz] );
>
>Usage: sizeof(sizer(t)).

This isn't guaranteed to work. 3.9 para 5 says the alignment
requirement of a complete object type is implementation defined. The
compiler is entitled to require all structs to have 4 byte alignment if
it chooses e.g. it might want to do all struct copying as multiples of
4 bytes. If a struct object has anything other than an alignment
requirement of 1 then packing may occur at the end of the array_size
struct above and sizeof will give the wrong value for cases where sz is
not a multiple of the alignment requirement.

Even when sz is a multiple of the alignment requirement there is a
theoretical possibility that padding can occur at the end of the array
- the C++ std hints )in 9.2 para 12 & 17 ) that the only thing that can
result is padding in a POD struct is an alignment requirement, however,
it doesn't explicitly say padding can't occur for other reasons. The
C99 std explicitly says there may be unnamed padding at the end of a
struct and provides no way of creating a struct where padding is
guaranteed to never occur with a conforming compiler. A compiler might
want all structs to be multiples of 4 bytes, regardless of the alignment
requirement of the struct.

Graeme

Tom Plunket

unread,
Jan 3, 2004, 9:24:40 PM1/3/04
to
Bronek Kozicki wrote:

> Probably the most popular solution of problem stated in topic is
> compile-time expresion:
> sizeof(table)/sizeof(table[0])
>
> However, I do not like so much typing. I know I can use macro, but
> macros are (at least in my project) not welcome. I'm searching for safer
> solution, possibly using templates.

Macros are appropriate for some things, IMO. This is one of
those things.

I have a solution that's worked in the past, but I don't know
where it falls down and why more complex code is required (as
presented by the others):

//-------------------------------------
// ArraySize
template<typename T, int N>
size_t ArraySize(T (&)[N])
{
return N;
}

Inlined, it is "free".

-tom!

Graeme Prentice

unread,
Jan 3, 2004, 9:25:02 PM1/3/04
to
On 3 Jan 2004 10:02:00 -0500, Graeme Prentice wrote:

>On 2 Jan 2004 23:11:28 -0500, Bronek Kozicki wrote:
>
>>Probably the most popular solution of problem stated in topic is
>>compile-time expresion:
>>sizeof(table)/sizeof(table[0])


You can also do this but the syntax is so ugly I forgot about it.

#include <iostream>

template <typename T, size_t S>

char (& table_size(T (&t) [S])) [S]
{
return S;
}

int main()
{
int x1[67];
static const int z = sizeof( table_size(x1) );
std::cout << z;
}

Graeme

James Dennett

unread,
Jan 3, 2004, 9:26:39 PM1/3/04
to
Ralf wrote:
> "Bronek Kozicki" <br...@rubikon.pl> schrieb im Newsbeitrag
> news:169gadiucxo8$.2gy1il2k64sb.dlg@40tude.net...
>
>>Probably the most popular solution of problem stated in topic is
>>compile-time expresion:
>>sizeof(table)/sizeof(table[0])
>>
>>However, I do not like so much typing. I know I can use macro, but
>>macros are (at least in my project) not welcome. I'm searching for safer
>>solution, possibly using templates.
>>
>>If I need to know size in runtime, I could use following:
>>template <typename T, size_t S>
>>int table_size(T (&t) [S]) {return S;}
>>
>>however, I need to know this size in compile time. Any ideas ?
>
>
> I think, you can't get it out in compile time, because you want it from an
> array name (constant pointer) and not from a type.

That would be a problem is an array name did evaluate to
a pointer value. Fortunately if a is an array, then the
expression
a
has array type, not pointer type. If this were not so
then the C technique of doing (sizeof a)/(sizeof *a) would
not work. The C++ techniques with templates ensure that a
really is an array, not a pointer, and so give compile-time
diagnostics if you mistakenly try to find an array size
given a pointer only.

> If you want it from a type, you will take the struct to get it. From a array
> name you need a function, wich has the runtime aspect. Indeed, you can make
> the function inline.

Various other posts in this thread have shown how to compute
the size of an array at compile-time, as a compile-time constant.
For many purposes the simple run-time function used by the OP
is sufficient, but slightly more advanced tricks are necessary
to get the desired compile-time constant when that is needed.

> The compiler will optimize it and calculate the value
> at compile time.
> But you can't use it inside of preprocessor directives.

Indeed even a compile-time solution based on templates is no
help to the preprocessor, but then the preprocessor doesn't
know anything of sizeof no matter how you slice it.

> Ralf
>
> www.oop-trainer.de/
> www.cplusplus-kurse.de/ooptrainer/

-- James.

Thorsten Ottosen

unread,
Jan 4, 2004, 6:12:06 AM1/4/04
to
"Jonathan Turkanis" <tech...@kangaroologic.com> wrote in message
news:bt5hmk$3e52m$1...@ID-216073.news.uni-berlin.de...

[snip]


> You can use the following code, adapted from Thorsten Ottosen's container
> traits library in the boost sandbox (see
> http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/)
>
> namespace detail
> {
> template< std::size_t sz >
> class array_size
> {
> char give_size[sz];
> };
> }
>
> template< typename T, std::size_t sz >
> detail::array_size<sz>
> sizer( const T (&array)[sz] );
>
> template< typename T, std::size_t sz >
> detail::array_size<sz>
> sizer( T (&array)[sz] );
>
> Usage: sizeof(sizer(t)).

Currently there are problems with alignment in this implementation. This is
more correct, but does not compile on some compilers:

template <class T, int sz>
char[sz] sizer(T (&array)[sz]);

br

Thorsten

Graeme Prentice

unread,
Jan 4, 2004, 6:14:08 AM1/4/04
to
On 3 Jan 2004 21:23:28 -0500, Pete Becker wrote:

>Bronek Kozicki wrote:
>>
>> Probably the most popular solution of problem stated in topic is
>> compile-time expresion:
>> sizeof(table)/sizeof(table[0])
>>
>> However, I do not like so much typing. I know I can use macro, but
>> macros are (at least in my project) not welcome. I'm searching for safer
>> solution, possibly using templates.
>>
>
>But your original solution wouldn't be welcome, either, since it uses
>the macro sizeof. <g> Working around arbitrary constraints can be a
>useful educational experience, but real-world programmers do use macros.

sizeof is not a macro - it is a built in unary operator and a language
keyword in both C and C++. You cannot #undef sizeof but you can #define
sizeof (producing undefined behaviour) because sizeof is treated
unconditionally as a language keyword in translation phase 7, after
macro substitution occurs in phase 4.

Graeme

Jonathan Turkanis

unread,
Jan 4, 2004, 6:14:55 AM1/4/04
to
"Pete Becker" <peteb...@acm.org> wrote in message
news:3FF6F675...@acm.org...

> Jonathan Turkanis wrote:
> >
> > You can use the following code, adapted from Thorsten Ottosen's
container
> > traits library in the boost sandbox (see
> > http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/)
> >
> > namespace detail
> > {
> > template< std::size_t sz >
> > class array_size
> > {
> > char give_size[sz];
> > };
> > }
> >
> > template< typename T, std::size_t sz >
> > detail::array_size<sz>
> > sizer( const T (&array)[sz] );
> >
> > template< typename T, std::size_t sz >
> > detail::array_size<sz>
> > sizer( T (&array)[sz] );
> >
> > Usage: sizeof(sizer(t)).
> >
>
> But that uses a macro. <g>

Macro?

> More to the point, it's an awful lot of code
> to write for very little benefit.
>
> #define ARR_SIZE(x) (sizeof(x)/sizeof(x[0]))
>
> Okay, sure, if you use this with a pointer instead of an array you'll
> get the wrong answer. Hypothetically, that is. I've never had that
> problem. Don't waste time writing clever templates when a macro does the
> job more clearly and with less code.

I wouldn't recommend this if the only purpose were to obtain the size of an
array. It's a feature of a library which is useful for other purposes. As
long as its available, you might as well use it.

Also, if new macros are prohibited or strongly discouraged in the OP's
project, your solution (which was explicitly mentioned in the post) may not
be acceptable.

I don't disagree with your general point.

Jonathan

Jonathan Turkanis

unread,
Jan 4, 2004, 6:17:42 AM1/4/04
to

"Graeme Prentice" <inv...@yahoo.co.nz> wrote in message
news:i2nevv0fa5hjlk6sp...@4ax.com...

Thanks. The possibility of this sort of problem occured to me briefly, but I
was lazy and didn't look into it.

I'll email Thorsten and mention this problem -- and your solution. (BTW, I
may have mangled or misrepresented his code, so he shouldn't be blamed for
the error.)

Best regards,
Jonathan

Francis Glassborow

unread,
Jan 4, 2004, 11:14:05 AM1/4/04
to
In message <r3fevvs57g16ibqf5...@4ax.com>, Tom Plunket
<to...@fancy.org> writes

>Macros are appropriate for some things, IMO. This is one of
>those things.

However, like 'using directives', I think they should be directly
provided in implementation files and not in header files where they can
have unexpected consequences in TUs that are formed by including that
header file.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
or http://www.robinton.demon.co.uk
Happy Xmas, Hanukkah, Yuletide, Winter/Summer Solstice to all.

Pete Becker

unread,
Jan 4, 2004, 11:26:45 AM1/4/04
to
Graeme Prentice wrote:
>
> sizeof is not a macro

Oops. Bad day.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Bronek Kozicki

unread,
Jan 4, 2004, 4:46:03 PM1/4/04
to
On 3 Jan 2004 21:24:40 -0500, Tom Plunket wrote:
> I have a solution that's worked in the past, but I don't know
> where it falls down and why more complex code is required (as
> presented by the others):

Well, this solution is identical to the one I presented at the end of
orginal post. It does not deliver requested data in compile time. In
other words, I cannot use it when integer expression is expected during
compilation (like value of enum).


B.

Bronek Kozicki

unread,
Jan 4, 2004, 4:46:45 PM1/4/04
to
On 3 Jan 2004 21:24:40 -0500, Tom Plunket wrote:
> I have a solution that's worked in the past, but I don't know
> where it falls down and why more complex code is required (as
> presented by the others):

Well, this solution is identical to the one I presented at the end of

orginal post. It does not deliver requested data in compile time. In
other words, I cannot use it when integer expression is expected during
compilation (like value of enum).


B.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Jonathan Turkanis

unread,
Jan 5, 2004, 4:29:15 AM1/5/04
to
"Thorsten Ottosen" <nes...@cs.auc.dk> wrote in message:
[snip]

> >
> > namespace detail
> > {
> > template< std::size_t sz >
> > class array_size
> > {
> > char give_size[sz];
> > };
> > }
> >
> > template< typename T, std::size_t sz >
> > detail::array_size<sz>
> > sizer( const T (&array)[sz] );
> >
> > template< typename T, std::size_t sz >
> > detail::array_size<sz>
> > sizer( T (&array)[sz] );
> >
> > Usage: sizeof(sizer(t)).
>
> Currently there are problems with alignment in this implementation. This
is
> more correct, but does not compile on some compilers:
>
> template <class T, int sz>
> char[sz] sizer(T (&array)[sz]);
>

Aren't arrays prohibited as return types (8.3.5/6)? At any rate, I can't
find a compiler which accepts your corrected version. What's wrong with
letting sizer return an array by reference, as Graeme suggested?

Regards,
Jonathan

Jonathan Turkanis

unread,
Jan 5, 2004, 4:31:25 AM1/5/04
to
"Graeme Prentice" <inv...@yahoo.co.nz> wrote in message:

>
> sizeof is not a macro - it is a built in unary operator and a language
> keyword in both C and C++. You cannot #undef sizeof but you can #define
> sizeof (producing undefined behaviour) because sizeof is treated
> unconditionally as a language keyword in translation phase 7, after
> macro substitution occurs in phase 4.
>

I wish sizeof were a macro! If macros were that smart, I'd consider giving
up templates.

Jonathan

Rani Sharoni

unread,
Jan 5, 2004, 7:36:10 PM1/5/04
to
Jonathan Turkanis wrote:
> "Graeme Prentice" <inv...@yahoo.co.nz> wrote in message
> [...]

> Thanks. The possibility of this sort of problem occured to me
> briefly, but I was lazy and didn't look into it.
>
> I'll email Thorsten and mention this problem -- and your solution.
> (BTW, I may have mangled or misrepresented his code, so he shouldn't
> be blamed for the error.)

In case that you care about old compilers (e.g. VC6) you can consider the
following different approach.
The trick is to use the old C macro with array type checking.

template<typename T>
void force_array(T*, T* const volatile *); // detect pointers

struct eat { eat(void const volatile *); };
char force_array(eat, ...); // detect arrays

#define force_array(arr) \
(sizeof(force_array(arr, &arr)))

#define array_length(arr) \
(sizeof(arr)/sizeof(arr[0])*force_array(arr))

// test cases
int A[10];
int AA[10][5];
extern int const CA[10];

typedef int test[array_length(A) == 10];
typedef int test[array_length(AA) == 10];
typedef int test[array_length(AA[0]) == 5];
typedef int test[array_length(CA) == 10];

// interesting test case
struct X {
operator void*();
char operator[](int);
void* operator&();
};

X x;
// typedef int testx[array_length(x) == 10];

Enjoy,
Rani

0 new messages