C++: Array initialization with follow-up numbers: a too lazy to copy-paste version

Started by
9 comments, last by Zahlman 15 years, 5 months ago
Howdy all, I'm looking for a way to initialize an array with follow-up numbers: MyClass myArray[NUMBER_OF_MYCLASSES] = { MyClass(0), MyClass(1), MyClass(2) }; I have just over 100 of these classes. But since I'm too lazy to just copy-paste, and because I'm always looking for an overly complex solution to a simple problem I decided to ask you guys. I can't wrap my head around a template solution that results in an array, nor can I think up a macro that would do this... So, what are you thoughts?
STOP THE PLANET!! I WANT TO GET OFF!!
Advertisement
You can only initialise arrays of objects using the default constructor.

Either: loop through the default-initiliased array and set the data you need to like this:

MyClass myArray[NUMBER_OF_MYCLASSES];for(int i=0; i<NUMBER_OF_MYCLASSES; ++i)    myArray.setData(i);


or, store pointers in the array to avoid default construction:

MyClass* myArray[NUMBER_OF_MYCLASSES];for(int i=0; i<NUMBER_OF_MYCLASSES; ++i)    myArray = new MyClass(i);


but now you have the side-affect of memory management.

Regardless, if this is C++, perhaps you can use STL and declare a std::vector instead? It may not directly solve this issue, but it will likely help you in future.
You could always use this completely untested class:

class MyClassArray{	char _bytes[NUMBER_OF_MYCLASSES * sizeof(MyClass)];		void destruct(size_t count)	{		for (size_t i = count; i > 0; --i)		{			((MyClass *)_bytes).~MyClass();<br>		}<br>	}<br>	<br><span class="cpp-keyword">public</span>:<br>	MyClassArray()<br>	{<br>		size_t i = <span class="cpp-number">0</span>;<br>		<span class="cpp-keyword">try</span><br>		{<br>			<span class="cpp-keyword">for</span> (; i &lt; NUMBER_OF_MYCLASSES; ++i)<br>			{<br>				<span class="cpp-keyword">new</span> (_bytes + i * <span class="cpp-keyword">sizeof</span>(MyClass)) MyClass(i);<br>			}<br>		}<br>		<span class="cpp-keyword">catch</span> (…)<br>		{<br>			destruct(i);<br>			<span class="cpp-keyword">throw</span>;<br>		}<br>	}<br>	<br>	~MyClassArray()<br>	{<br>		destruct(NUMBER_OF_MYCLASSES);<br>	}<br>	<br>	MyClass &amp; <span class="cpp-keyword">operator</span>[](size_t i)<br>	{<br>		<span class="cpp-keyword">return</span> ((MyClass *)_bytes)<span style="font-weight:bold;">;<br>	}<br>	<br>	<span class="cpp-keyword">const</span> MyClass &amp; <span class="cpp-keyword">operator</span>[](size_t i) <span class="cpp-keyword">const</span><br>	{<br>		<span class="cpp-keyword">return</span> ((<span class="cpp-keyword">const</span> MyClass *)_bytes)<span style="font-weight:bold;">;<br>	}<br>};<br><br></pre></div><!–ENDSCRIPT–><br><br>However code like this is a good sign you're being too smart for your own good.  100 isn't many at all, so any of zdlr's solutions are very likely all you need.
Quote:Original post by Structural
I have just over 100 of these classes.

100 classes? That doesn't sound right. What do these classes represent?
Quote:Original post by DevFred
Quote:Original post by Structural
I have just over 100 of these classes.

100 classes? That doesn't sound right. What do these classes represent?


I'm pretty sure he meant (Ahem: this is why you should strive to use proper terminology) that he wanted to put ~100 instances of MyClass in the array.
Quote:Original post by Structural
I'm looking for a way to initialize an array with follow-up numbers:

MyClass myArray[NUMBER_OF_MYCLASSES] = { MyClass(0), MyClass(1), MyClass(2) };

Is that array the only place where you instantiate MyClass? Then you can use a static counter to automate the counting process and call a no-argument constructor instead (which C++ does automatically if you don't provide an initialisation list).

#include <iostream>class MyClass{    static int counter;public:    int value;    MyClass()    {        value = counter++;        // do other construction stuff    }};int MyClass::counter = 0;MyClass myArray[100];int main(){    std::cout << myArray[0].value;}

This outputs 0 on my implementation, but I'm not sure if the order of evalution of array intializer list elements is defined by the standard. Any language lawyers around?
You could use a standard container and standard algorithm generate_n together with a generator.
struct MyClass;class MyClassGenerator {    unsigned nextValue;public:    MyClassGenerator()        : nextValue(0)    { }    MyClass operator () () {        return MyClass(nextValue++);    }};...typedef std::vector<MyClass> Container;Container myArray;myArray.reserve(NUMBER_OF_MYCLASSES);std::generate_n(std::back_inserter(myArray), NUMBER_OF_MYCLASSES, MyClassGenerator());


If you do want the objects to be stack-allocated, you could first create a storage and then placement-new the objects in there:
char storage[NUMBER_OF_MYCLASSES * sizeof(MyClass)];MyClass** myArray;  // I'm a double-star programmer, yay!for (std::size_t i = 0; i < NUMBER_OF_MYCLASSES; ++i)    myArray = new (storage) (i);<br><br><span class="cpp-comment">// *(myArray[index]) is your object, and it's on the stack.</span><br><br>…<br><br><span class="cpp-comment">// Don't forget to manually call the destructors when you're done with it (or when an exception is thrown)!</span><br></pre></div><!–ENDSCRIPT–><br>I think it's quite apparent the second version is not something really recommendable. [grin]
Do you absolutely need an array of MyClass? Can you use a std::vector<MyClass>, and do something like this:
class MyClassFactory{  int m_state;public:  MyClassFactor(int i = 0) : m_state(i) {}  MyClass operator()() { return MyClass(m_state++); }};...vector<MyClass> foo;generate_n(insert_iterator<vector<MyClass> >(foo, boo.begin(), 100, MyClassFactory());


The big disadvantage is calling the constructor and copy constructor to populate the vector initially. But, any solution where you declare the array with the default constructor and then go and change all the values will have that problem as well. (You could get around that by having the loop affect the state of the existing objects in the vector, but then you might have to make some stuff public in MyClass that you don't want to.) There's also a speed hit as the vector grows.

Another solution, which I'm not sure would work, would be to declare a static variable in MyClass which keeps track of the number constructed, so that the nth call to MyClass() is equivalent to MyClass(n - 1). I think

MyClass foo[3];


would then be equivalent to

MyClass foo[3] = { MyClass(0), MyClass[1], MyClass[2] };

 ~~C--O   -
Quote:Original post by Structural

macro


Macro you say?

#include <iostream>#include <boost/preprocessor/iteration/local.hpp>struct MyClass {  MyClass(int ii) : i(ii) {}  int i;};#define BOOST_PP_LOCAL_MACRO(n) , MyClass(n)#define BOOST_PP_LOCAL_LIMITS (1, 99)MyClass myArray[] = {	MyClass(0)#include BOOST_PP_LOCAL_ITERATE()};int main(int, char**) {  for (int i = 0; i < 100; i++) std::cout << myArray.i << " ";}
Quote:Original post by Zahlman
Quote:Original post by DevFred
Quote:Original post by Structural
I have just over 100 of these classes.

100 classes? That doesn't sound right. What do these classes represent?


I'm pretty sure he meant (Ahem: this is why you should strive to use proper terminology) that he wanted to put ~100 instances of MyClass in the array.


Indeed, my bad.


The array is an ItemVector, MyClass is an Item, and every Item has a description. The index of the Item in the array determines its description.
I would like the usage of the ItemVector class to be as follows:

myItemVector[index].Description.Name();
myItemVector[index].Value;

This piece of software is going to do some heavy duty processing, and I also plan to put it on a mobile device, and there are going to be a lot of instances of an ItemVector. Therefore I am a bit careful with introducing loops for things that can be resolved at compile time. Hence the static initialization.


I now implemented the array initialization as zdlr suggested.
The static counter is not going to work because, as I said, I plan on making a lot of instances of the ItemVector.
I'm going to dismantle BOOST_PP_LOCAL_ITERATE as Antheus suggested to see if that works.

ps. I know, premature optimization is bad, I'm just curious now if this is possible.
STOP THE PLANET!! I WANT TO GET OFF!!

This topic is closed to new replies.

Advertisement