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

Started by
36 comments, last by random_thinker 16 years, 6 months ago
Thought I would post this little C++ template 'Gem' for comment. Where you need to get the size of an array, as in:

size_t num_elements = sizeof(some_array)/sizeof(array_element);
You can use a function template approach, as:

template<typename T, std::size_t N>
inline std::size_t containerSize( T (&)[N] )
{ 
	return N;
}

// Then wherever you need an array size, just...
size_t num_elements = containerSize(some_array);
Template power! --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'.
Advertisement
Or just use boost::array or std::vector, or other actual container classes and not worry about all the pitfalls that can occur when using raw arrays.
I generally find this technique to be too dangerous to use.

It will only work for arrays -- that is, not arrays that have decayed. It can be pretty easy for an uninitiated programmer to use it when it isn't appropriate. It is very easy for any programmer to leave the use in place when refactoring the code such that it's no longer valid.

The situations in which I use arrays that this technique is compatible with are so rare in C++ that I've never bothered to employ it in practice.
Why do you think it's dangerous? This funtion works only for constant size arrays.
Sometimes it's better/faster to use a local array than dynamic, so this function can be useful.
The technique itself is dangerous -- this specific implementation is a common way to attempting to protect against the obvious misuses of the technique.

The general uselessness of the technique stems from the fact that it only applies when you already know the size -- it's primary advantage is that it helps prevent bugs when you change the init size of the array but fail to change any loops or other code that gate on the size of the array. But as SiCrane said, in those cases you can generally use something like boost::array, which is still a nondynamic array and have an even stronger model of the concept, as well as other advantages.
Quote:Original post by rozz666
Why do you think it's dangerous? This funtion works only for constant size arrays.
Sometimes it's better/faster to use a local array than dynamic, so this function can be useful.

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.

If you're talking about allocating a small array on the stack, okay, sure, that can be harmless. But then the size is readily available.
Quote:Original post by drakostar
Quote:Original post by rozz666
Why do you think it's dangerous? This funtion works only for constant size arrays.
Sometimes it's better/faster to use a local array than dynamic, so this function can be useful.

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.

If you're talking about allocating a small array on the stack, okay, sure, that can be harmless. But then the size is readily available.


Yes, I was thinking about allocating on the stack.

When in a practical situation size of an array (except char *) is not available?

This template can be useful anywhere you use sizeof(v) / sizeof(v[0]), e.g.
You have an array int buf[BUFSIZE1]. But then you decided it has to be int buf[BUSIZE1 + BUFSIZE2]. You don't have to change the rest of the code, unless you need to add more functionality.
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.

Cross scope, or worse, function boundary and all that info is done.

Quote:You have an array int buf[BUFSIZE1]. But then you decided it has to be int buf[BUSIZE1 + BUFSIZE2].

Then adjust your local size variable and stop writing hacky code.
I'm currently playing around with this in an error code system...using a map to store the messages by index, creating error states and then using these to make error policies (ie FatalError, TolerableError, etc.). The beauty of using this combo (in addition to efficiency) is that the only place that needs extending is the errors[] array, the template automatically sets the correct size, so the entire setup is data driven.

// 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[];	static const std::size_t size;};// 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"},};// Get codes container size...const std::size_t ErrorMessages::size = containerSize(ErrorMessages::errors);// Define the codemap...struct ErrorMap{	typedef std::map<const int,const std::string> MapType;	ErrorMap() : 		data(ErrorMessages::errors,ErrorMessages::errors+ErrorMessages::size)	{  }		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 ErrorStates{	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'.
Yes I have tried this technique before, but the amount of time and thought it took to decide whether the array was a worthy choice for this technique was more than it took to create and use a size variable for the array. A size variable is also universal across both static and dynamic arrays. However, my application domain hasn't required the use of C++ in quite some time so this has been a non-issue lately.
....[size="1"]Brent Gunning

This topic is closed to new replies.

Advertisement