Jump to content
  • Advertisement
Sign in to follow this  
random_thinker

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

This topic is 3925 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!