views:

70

answers:

2

I have a static std::map<std::string, CreateGUIFunc> in a class that basically holds strings identifying gui types, and CreateGUIFunc is a reference to a factory function.

In my constructor I have

if ( m_factoryRef.size() == 0 ) {
  m_factoryRef["image"] = &CreateGUI<Image>;
  m_factoryRef["button"] = &CreateGUI<Button>;
}
...

However, this gives me an error saying assignment of read-only location ‘GUIManager::m_factoryRef.std::map<_Key, _Tp, _Compare, _Alloc>::operator[] [with _Key = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Tp = GUI*(), _Compare = std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Alloc = std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, GUI*()> >](((const std::basic_string<char, std::char_traits<char>, std::allocator<char> >&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"image"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>())))))))’|

I'm not sure why this is a read-only assignment. I also tried changing it to a normal member just to see if maybe it had to do with it being static, but same thing.

What is the problem?

Edit: Some definitions to make things a bit more clear

// these are all private

typedef GUI* CreateGUIFunc();

template<class T>
GUI* GUIManager::CreateGUI( std::string &filepath, int x, int y ) {
  return new T( filepath, x, y );
}

static std::map<std::string, CreateGUIFunc> m_factoryRef;

P.S. If there is any cleaner way to initialize a static map please let me know.

A: 

In C++, typedef GUI* CreateGUIFunc(); isn't a function with unspecified parameters, it's a function with NO parameters. So none of your functions match that type. What you want is typedef GUI* (*CreateGUIFunc)( std::string &filepath, int x, int y );

Next, try using the insert member function of the map instead of the subscript operator, that way you can't end up calling the constant version by accident.

Ben Voigt
ok, I was closer than I thought in interpreting what was wrong with the function pointer, but on the subscript operator, it should work, and if it doesn't, it should not work for a reason other than accidents...
ufotds
+1  A: 

Here are some alternative initializations for current C++. I'm not sure what you mean by "static" initialization when you said your code is in a constructor. I have some guesses, but it doesn't matter. Choose what feels "cleaner" to you.

if (m.empty()){
   m.insert (map<K,V>::value_type (k1, v1));
   m.insert (map<K.V>::value_type (k1, v1));
}

or,

map<K,V>&  getmap() {
    static map<K,V>* m = 0;
    if (!m){
         m = new map<K,V>(); // never deleted.
        // insert.
    }
    return *m;
}

or,

namespace /*anon*/ {
      map<K,V>* init_map () { 
         map<K,V>* m = new map<K,V>();
         // insertions here.
         return m; // return by val. can move in c++0x.
      }
}

map<K,V> Foo::m = *init_map ();

or,

namespace /*anon*/ {
          map<K,V>* init_map () { 
             map<K,V>* m = new map<K,V>();
             // insertions here.
             return m; // return by val. can move in c++0x.
          }
}

map<K,V>&  Foo::get_map () { /* static singleton accessor */
     static map<K,V> * m = init_map ();
     return *m;
}

The pattern should be obvious. You have to solve two problems: the trivial problem of initializing the damn thing only once, and the trickier problem of preventing the static initialization order fiasco. My person preference is something like the second case above.

John
+1 for providing a variety of solutions. I'd suggest staying clear of #1 and also think #2 is simple and concise. It is worth noting that all of these cases are prone to problems in multithreaded contexts (if Foo is constructed for the first time by two threads concurrently).