简体   繁体   中英

Compile time Meta-programming, with string literals

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM