C++ Gem...getting size of array....

Started by
36 comments, last by random_thinker 16 years, 6 months ago
Quote:Original post by Oluseyi
Quote:Original post by rozz666
This template can be useful anywhere you use sizeof(v) / sizeof(v[0])...

Which is only in places where you already know the size of the array, because you defined it in the same scope. It's quicker to just store the dimension you statically requested.


There are situations where this is a useful technique. The most common place I find it useful is where I have a static constant array whose size is implicit from the number of initializers. In that case it's handy to be able to get the size of the array using this technique without having to duplicate information by creating a size variable that must be updated if you add elements to the array. e.g.

namespace {const char* someConstantStrings[] = { "apple", "orange", "pear" };}void doSomethingWithStrings(){    for (int i = 0; i < lengthof(someConstantStrings); ++i)    {        doSomethingWithString(someConstantStrings);    }}


[edit] Which now I look at it is pretty much exactly the use the OP was putting it to in his error example above.

Game Programming Blog: www.mattnewport.com/blog

Advertisement
Personally, I still find having a macro to do: sizeof( array ) / sizeof( array[ 0 ] ) is better than using the template function (unfortunately).

int arrayTest[ ] ={    4,    9,    7,    3};STATIC_ASSERT( containerSize( arrayTest ) == 4 ); // error C2975: expected compile-time constant expressionSTATIC_ASSERT( sizeof( arrayTest ) / sizeof( arrayTest[ 0 ] ) == 4 ); // ok.void Foo( ){    int someArray[ containerSize( arrayTest ) ];    // error C2057: expected constant expression    // error C2466: cannot allocate an array of constant size 0    // error C2133: 'arraysize' : unknown size    int someArray[ sizeof( arrayTest ) / sizeof( arrayTest[ 0 ] ); // ok.}
Quote:Original post by mattnewport
There are situations where this is a useful technique. The most common place I find it useful is where I have a static constant array whose size is implicit from the number of initializers. In that case it's handy to be able to get the size of the array using this technique without having to duplicate information by creating a size variable that must be updated if you add elements to the array. e.g.

You should have just determined the size right after initialization, using this little "technique." Your example is contrived, by needing to have the array global with respect to the function definition. And since you know fully well how much we bash on global variables due to ownership problems (shoving it in an anonymous namespace doesn't make it any less global). So... no. Still close to useless.
Quote:Original post by Oluseyi
You should have just determined the size right after initialization, using this little "technique."

I disagree. Determining the size after initialization just introduces another variable with redundant information - the compiler knows the size of the array and with this technique you can refer to it directly, so why introduce another name with the possibility (even if slight) of the use of the name getting out of sync with the quantity you care about (the size of a fixed array).
Quote:Your example is contrived, by needing to have the array global with respect to the function definition. And since you know fully well how much we bash on global variables due to ownership problems (shoving it in an anonymous namespace doesn't make it any less global). So... no. Still close to useless.

The example isn't contrived, I've used a utility implementation of exactly this technique on many occasions in code that looks a lot like that example. I don't know why you're dragging global variables into the conversation, all that's required for this technique is that the compiler knows the array size at the point of use. For the sake of simplicity my example used an array in an anonymous namespace but the array could equally be a local variable in a function, a local static in a function or a class static.

The benefit of this technique in my opinion is that it follows the 'Don't Repeat Yourself' principle - the size of the array is implicit in the array definition and if the compiler knows the size at the point you wish to refer to it there is no benefit and some potential for error involved if you introduce a new variable to hold the size of the array.

Game Programming Blog: www.mattnewport.com/blog

Oops!

Just noticed that I could eliminate a variable and a few lines of code using this approach...

// Returns the number of elements, pass an array.template<typename T, std::size_t N>inline std::size_t containerSize( T (&)[N] ){ 	return N;}// Error messages...struct ErrorMessages{	struct MapThunk	{		const int idx;		const char * message;		operator std::pair<const int,const std::string>()		{			return std::make_pair(idx,message);		}	};		static MapThunk errors[];};// Create map, control and add codes here...ErrorMessages::MapThunk ErrorMessages::errors[] = {	{1,"This is error 1"},	{2,"This is error 2"},	{3,"This is error 3"},	{4,"This is error 4"},};// Define the codemap...struct ErrorMap{	typedef std::map<const int,const std::string> MapType;	ErrorMap() : 		data(ErrorMessages::errors,ErrorMessages::errors+containerSize(ErrorMessages::errors))	{	}		MapType data;};// Message accessors...struct Messages{	static std::string get(const int idx)	{		if (map.data.find(idx) == map.data.end())			throw std::runtime_error("Illegal error message index");		return map.data[idx];	}		static ErrorMap map;};// Initialize the message map...ErrorMap Messages::map;// Error states...template <const int idx,typename T=std::ostream>struct States{	static void die()	{		T stream;		stream << "\nFATAL ERROR: " + Messages::get(idx) + ", exiting..." << std::endl;		exit (1);	}			static void survive()	{		T stream;		stream << "\nWARNING: " + Messages::get(idx) + ", continuing..." << std::endl;		// Continue...	}};// Error Policies...

--random
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
typedef std::map<const int,const std::string> MapType;namespace Messages{  const MapType & Error()  {    static MapType the_map;    if (the_map.empty())     {      boost::assign::insert (the_map)         (1,"This is error 1")	(2,"This is error 2")	(3,"This is error 3")	(4,"This is error 4");    }    return the_map;  }}struct ErrorMap{	ErrorMap() : data(Messages::Error()) {}	MapType data;};
Thx ToohrVyk,

No matter how compact or efficient the code looks, someone here always finds a shorter method. I suppose that if one day I post code and no one responds, it must be pretty good!

Exceptionnel!

--random
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
Quote:Original post by random_thinker
No matter how compact or efficient the code looks, someone here always finds a shorter method. I suppose that if one day I post code and no one responds, it must be pretty good!


... Or it COULD be that I (or someone else) discovered that the code could be replaced with nothing at all, and that I (or someone else) could demonstrate this in a Zen-like manner by not posting ;)
Oh, I get it, you get rated up for those posts you never write, too? [wink]

Generally, there's a couple of thin lines between 'short enough to be obscure', 'elegantly short' and 'too verbose'. The only verbosity that deserves to be accepted, though, is a self-documented one.

Though I always enjoy those if (a == true) return true; else if (a == false) return false; else return false;
Quote:When? Really, if you can demonstrate any practical instance where an array is measurably faster than a properly-used std::vector, I would be amazed.

Prepare to be amazed by two examples :)
1) a routine that needs small amounts of temporary storage but cannot share it between calls due to reentrancy requirements. (dynamic allocation is more expensive than grabbing some memory from the stack)
2) bin- aka counting sort, where you'd want to resize the destination container without initializing its values (EASTL provides this capability, but you are talking about std::vector)


Quote:Personally, I still find having a macro to do: sizeof( array ) / sizeof( array[ 0 ] ) is better than using the template function (unfortunately).

The former has a huge problem: if you refactor the code such that "array" is now a pointer, it'll still compile but do the wrong thing. After encountering such a bug and spending quite a while auditing the remainder of the code, the following came to mind:

// (function taking a reference to an array and returning a pointer to// an array of characters. it's only declared and never defined; we just// need it to determine n, the size of the array that was passed.)template<typename T, size_t n> char (*ArraySizeDeducer(T (&)[n]))[n];// (although requiring C++, this method is much better than the standard// sizeof(name) / sizeof(name[0]) because it doesn't compile when a// pointer is passed, which can easily happen under maintenance.)#define ARRAY_SIZE(name) (sizeof(*ArraySizeDeducer(name)))

You still get a compile-time constant but are insured against the above bug.
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3

This topic is closed to new replies.

Advertisement