0

I want to initialize some lookup table in a shared library, I want to see if this is a valid way, and if it is continue using it in more libraries I am about to write.

typedef std::map<std::string, int> NAME_LUT;
NAME_LUT g_mLUT;
namespace
{
    bool OneTimeInit() 
    {
        ::g_mLUT.insert(NAME_LUT::value_type("open",          1));
        ::g_mLUT.insert(NAME_LUT::value_type("close",         2));
        return true;
    }
    bool bInit = OneTimeInit(); // Just to make initialization happen
}

It seems to work fine on both Visual Studio, and gcc (Linux). Only gcc complains that bInit is not used anywhere.

  1. Is it possible that initialization is optimized out (bInit not used), or language does not allow it (because of the side effect).
  2. It sure looks like a good cross platform way of handling onetime initialization, but I am not sure if this is the best approach.
  3. Does it make sense to make OneTimeInit declared static? (i.e. use static bool OneTimeInit() {...}), or namespace alone is a better approach to make it unique to this compilation unit
dashesy
  • 2,596
  • 3
  • 45
  • 61
  • Is that GCC 'complaint' a warning or an error? – Steve Townsend May 07 '12 at 16:34
  • 2
    Unfortunately, [there is more to this problem than meets the eye](http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14). – Sergey Kalinichenko May 07 '12 at 16:34
  • @SteveTownsend- no it is a usual warning (I believe gcc 4.6 started to complain, not any version before that) – dashesy May 07 '12 at 16:43
  • 1
    @dasblinkenlight- Thanks for the link but I am aware of the static initialization order fiasco. This is an anonymous namespace declared in a cpp file. Do you think it is still a problem? please let me know. – dashesy May 07 '12 at 16:46
  • *Aside*: Isn't `g_mLUT["open"] = 1; g_mLUT["closed"] = 2;` easier to read but otherwise identical? – Robᵩ May 07 '12 at 16:54
  • @Robᵩ - good catch, this is copy-pasted from a legacy code I will use the more readable method you suggested, thanks. – dashesy May 07 '12 at 18:01

4 Answers4

7

I don't quite like the idea of variables with static storage, but if you are going to do so, you can actually simplify the code by just writing a function that will initialize your object:

typedef std::map<std::string, int> NAME_LUT;
namespace {
   NAME_LUT create_lut() {
        NAME_LUT table;
        table.insert(NAME_LUT::value_type("open",          1));
        table.insert(NAME_LUT::value_type("close",         2));
        return table;
   }
}
NAME_LUT g_mLut = create_lut();

Note that this has all of the usual initialization order issues (accross different translation units, and specially with dynamic libraries)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I actually like your method better, but what would be a better approach so you think? – dashesy May 07 '12 at 16:55
  • I used your method, but what do you think is a better approach other than static storage? specially if this variable holds the dispatching (function pointers instead of integer) and I cannot enforce an `initialize` method. – dashesy May 07 '12 at 19:53
  • 1
    I guess there are times where you do need *globals*, if that is the case, I usually prefer `static` local variables, as they solve half of the initialization issues (construction), even if you have to still be aware of the order of destruction... It's hard to say without knowing what your particular problem is... – David Rodríguez - dribeas May 07 '12 at 20:08
  • Interestingly enough, you are right I could use static local variable in my case! There is a dispatcher function in the library. But I guess I have an allergy to static local variables, so leave it as global for now, if it bit me in the future I will come here an apologize – dashesy May 07 '12 at 20:30
3

Yes, it is legal but since you mention that it's in a library, you must ensure that the translation you are creating this in will be linked:

How to force inclusion of "unused" object definitions in a library

Community
  • 1
  • 1
Edward Strange
  • 40,307
  • 7
  • 73
  • 125
1

If you have C++11 initializer lists, would this work better for you?

NAME_LUT g_mLUT = { {"open", 1}, {"close", 2}, };
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
0

If bInit gets optimized out (possible, especially if your lib gets dlopened) then your initialization code won't run.

Is there a problem with having the user make a setup call to your library in a sane place (after main starts)? Doign it thihs way removes any possibility of one of many possible hard-to-debug order-of-initialization bugs from cropping up in your code. If that's not an option I would really suggest hiding the init into a function call with a static local, something like this:

typedef std::map<std::string, int> NAME_LUT;
namespace
{
    bool OneTimeInit(NAME_LUT& mLUT) 
    {
        ::mLUT.insert(NAME_LUT::value_type("open",          1));
        ::mLUT.insert(NAME_LUT::value_type("close",         2));
        return true;
    }

    NAME_LUT& get_global_mLUT()
    {
        static NAME_LUT g_mLUT;
        static bool bInit = OneTimeInit(g_mLUT); // Just to make initialization happen
        return g_mLUT;
    }
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • 1
    The method suggested by "David Rodríguez" does not get optimized out. Thanks for the suggestion but the library is opened by third party software, so I cannot enforce the initialize method. – dashesy May 07 '12 at 18:10