Sign in to follow this  
Structural

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

Recommended Posts

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?

Share this post


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

Share this post


Link to post
Share on other sites
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)[i - 1].~MyClass();
}
}

public:
MyClassArray()
{
size_t i = 0;
try
{
for (; i < NUMBER_OF_MYCLASSES; ++i)
{
new (_bytes + i * sizeof(MyClass)) MyClass(i);
}
}
catch (...)
{
destruct(i);
throw;
}
}

~MyClassArray()
{
destruct(NUMBER_OF_MYCLASSES);
}

MyClass & operator[](size_t i)
{
return ((MyClass *)_bytes)[i];
}

const MyClass & operator[](size_t i) const
{
return ((const MyClass *)_bytes)[i];
}
};



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.

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites
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[i] = new (storage[i + sizeof(MyClass)]) (i);

// *(myArray[index]) is your object, and it's on the stack.

...

// Don't forget to manually call the destructors when you're done with it (or when an exception is thrown)!

I think it's quite apparent the second version is not something really recommendable. [grin]

Share this post


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

Share this post


Link to post
Share on other sites
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].i << " ";
}

Share this post


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

Share this post


Link to post
Share on other sites
Quote:
Original post by Structural
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.


Um, this is really bad. Objects shouldn't have to know where they are in a container. They shouldn't even have to know that they're in a container (because, in particular, that means they *have to be* in a container, which already ties your hands). Further, if the index determines a description, then either every item's description is unique, or you have some logic somewhere else that maps the index value to a description. In either case, you could just replace the index member with a reference or pointer to the description text :) Then you have no obvious design faults/unintuitivenesses, simpler lookup code (although possibly more complex initialization code) and no extra memory usage.

And as for doing that assignment of descriptions to objects, have you considered reading data in from a file?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this