tags:

views:

872

answers:

5

I've got a static class member which is some container, like

(Foo.h)

class Foo
{
   ...
private:
   static list<string> s_List;
}

I need to populate the list with a number of specific values. Actually it should be also const, but that might overcomplicate the problem further. All the class member functions are static, so initializing it in a constructor doesn't make sense.

A: 

The ways I(the author of the question) have vainly tried to do this.


I tried to do smth like (in Foo.cpp):

list<string> Foo::s_List = list<string>();
Foo::s_List.insert("apple");
Foo::s_List.insert("bannana");
Foo::s_List.insert("grapes");

But that gives a compiler error.


Then I thought of making an Initialize() method and calling it right from the code

void Foo::Initialize()
{
    s_List.insert("rats");
    s_List.insert("cats");
}

Foo::Initialize();

// error: compiler considers it to be a redefenition of the method, not a call.


The only viable idea left (havent yet tried) would be to check if the list is empty in each method that uses it, and if it's the case, call Initialize(). But that's ugly!

Maleev
Initialize should be a static function.Sorry for the question, but are you trying to do function calls *outside* functions or methods?
happy_emi
Yes, in fact I was :)
Maleev
A: 

one possible solution would be to use an accessor method that checks to see if it is initialized, and does so if it isn't.

cobbal
+6  A: 

Another alternative is to create a simple initialiser class:

list <string> Foo::s_List;

struct Init {
   Init() {
      Foo::s_List.insert("apple");
      Foo::s_List.insert("bannana");
      Foo::s_List.insert("grapes");
   }
};

static Init doInit;

Note that, as the list is private, this will probably require you to make Init a friend of Foo. It's also often convenient to make such classes be contained by the class they are initialising.

However, I just re-read your question and another thought occurs - if the list is const, you will presumably not be changing it, in which case a simple array of strings, initialised with the strings in sorted order may be a better solution. It will certainly be faster to search (using std::binary_search) than a list, and can of course be easily initialised.

anon
+1, beat me to it :)
j_random_hacker
Good idea. It's guaranteed to work, because static members and globals are initialised in order in each source file. Since the list is private in the original question, it will need a friend declaration.
James Hopkin
Looks great. Thanks
Maleev
@james yes - good point.
anon
Neil Butterworth: as to your last edit, at least in my case that won't be the best solution because I need to check if some string is in the list, so I'd better use a standard contains/find algorithm.
Maleev
You can use the standard algorithms on arrays - in fact using std::binary_search on a sorted array will be very fast
anon
AFAIK C++ doesn't guaranteer global variables allocation order. How can you be sure that doInit will be allocated AFTER s_List?
happy_emi
The order is guaranteed in the same compilation unit.
anon
I see your point Neil, thank you
happy_emi
Another way to avoid the copy would be to make s_List a shared_ptr.
Dave Mooney
What "copy" is that?
anon
+6  A: 

a common solution is to do something like this:

// header
class Foo
{
...
private:
   static list<string> s_List;
}

// cpp
list<string> init()
{
     list<string> tmp;
     ... fill tmp with strings

     return tmp;
 }

 list<string> Foo::s_List(init());

the other method is like Neil Butterworth suggested.

Idan K
I guess yours is a more versatile solution, because a private member can be initialized without any fuss... and is it possible for s_List to be const? Then init() should return const list<string> as well. Right?
Maleev
Also, from the viewpoint of performance: wouldn't it be better to return a reference to tmp, because it will be coppied anyway by the implicit copy constructor (or assignment operator, correct me) in the last line? Then we it won't be coppied twice. Any subleties I didn't count?
Maleev
yes, you could change it to a const list<>.however you can't return a reference since "tmp" is a local variable and will die once you leave init().
Idan K
what you could do is change "tmp" to be static and then return a reference to it. but that will complicate things if you'll use init() to init more than one list and in my opinion is a bit overkill (unless you push a lot of strings, and then maybe the other approach is preferred).
Idan K
I see your point. Thanks!
Maleev
+1  A: 

It depends on what values you need to put in that list. Are they static or do they require some form of computation?

If they are static, you can do this:

namespace {
   const char* const initVals[] = { "A", "B", "C" };
}

list<string> Foo::s_list(initVals, initVals + 3);
Brian Neal