views:

184

answers:

1

I'm writing some code which could really do with some simple compile time metaprogramming. It is common practise to use empty-struct tags as compile time symbols. I need to decorate the tags with some run-time config elements. static variables seem the only way to go (to enable meta-programming), however static variables require global declarations. to side step this Scott Myers suggestion (from the third edition of Effective C++), about sequencing the initialization of static variables by declaring them inside a function instead of as class variables, came to mind.

So I came up with the following code, my hypothesis is that it will let me have a compile-time symbol with string literals use-able at runtime. I'm not missing anything I hope, and that this will work correctly, as long as I populate the runtime fields before I Initialize the depending templates classes ? .

#include <string>

template<class Instance>

class TheBestThing {
public:
   static void set_name(const char * name_in) {
      get_name() = std::string(name_in);
   }
   static void set_fs_location(const char * fs_location_in) {
      get_fs_location() = std::string(fs_location_in);
   }
   static std::string & get_fs_location() {
      static std::string fs_location;
      return fs_location;
   }
   static std::string & get_name() {
      static std::string name;
      return name;
   }  
};
struct tag {};
typedef TheBestThing<tag> tbt;

int main()
{
   tbt::set_name("xyz");
   tbt::set_fs_location("/etc/lala");

   ImportantObject<tbt> SinceSlicedBread;
}

edit: Made community wiki.

+1  A: 

I've finally understood what the problem was... and your solution does not solve much, if any.

The goal of using local static variable is to provide initialization on first use, thus being safe from the "Initialization Order Fiasco" (by the way, it does not solve the "Destruction Order Fiasco").

But with your design, if you effectively prevent the crash you do not however prevent the issue of using a variable before its value is used.

ImportantObject<tbt> SinceSliceBread; // using an empty string

tbt::set_name("xyz");

Compare with the following use:

std::string& tbt::get_name() { static std::string MName = "xyz"; return MName; }

Here the name is not only created but also initialized on first use. What's the point of using a non initialized name ?

Well, now that we know your solution does not work, let's think a bit. In fact we would like to automate this:

struct tag
{
  static const std::string& get_name();
  static const std::string& get_fs_location();
};

(with possibly some accessors to modify them)

My first (and easy) solution would be to use a macro (bouh not typesafe):

#define DEFINE_NEW_TAG(Tag_, Name_, FsLocation_)              \
  struct Tag_                                                 \
  {                                                           \
    static const std::string& get_name() {                    \
      static const std::string name = #Name_;                 \
      return name;                                            \
    }                                                         \
    static const std::string& get_fs_location() {             \
      static const std::string fs_location = #FsLocation_;    \
      return fs_location;                                     \
    }                                                         \
  };

The other solution, in your case, could be to use boost::optional to detect that the value has not been initialized yet, and postpone initialization of the values that depend on it.

Matthieu M.
Yes, I have not addressed the issue of initialization sequencing, My main concern was to have an easy way to use strings which are available at compile time and associate them with a compile time tag. Boost::MPI string literals are a bit of a hack and c++0x is addressing the issue. so, I chose the static-string-declared-in-function technique from Myers' book. Thank you for the analysis, and your Macro can certainly make the code read more like a story.
Hassan Syed
Accepted as answer, as it adds value to the question. However, a lot of people have discussed this question and I have made successive edits to the code in the question. Boost optional is a very good way of guarding against initialization issues. Perhaps I will add it, however since the code is for internal use in my project I might not do it immediately.
Hassan Syed