3

I'm exploiting the behavior of the constructors of C++ global variables to run code at startup in a simple manner. It's a very easy concept but a little difficult to explain so let me just paste the code:

struct _LuaVariableRegistration
{
    template<class T>
    _LuaVariableRegistration(const char* lua_name, const T& c_name) {
        /* ... This code will be ran at startup; it temporarily saves lua_name and c_name in a std::map and when Lua is loaded it will register all temporarily global variables in Lua. */
    }
};

However manually instantiating that super ugly class every time one wants to register a Lua global variable is cumbersome; that's why I created the following macro:

#define LUA_GLOBAL(lua_name, c_name) static Snow::_LuaVariableRegistration _____LuaGlobal ## c_name (lua_name, c_name);

So all you have to do is put that in the global scope of a cpp file and everything works perfectly:

LUA_GLOBAL("LuaIsCool", true);

There you go! Now in Lua LuaIsCool will be a variable initialized to true!

But, here is the problem:

LUA_GLOBAL("ACCESS_NONE", Access::None);

Which becomes:

static Snow::_LuaVariableRegistration _____LuaGlobalAccess::None ("ACCESS_NONE", &Access::None);

:(( I need to concatenate c_name in the macro or it will complain about two variables with the same name; I tried replacing it with __LINE__ but it actually becomes _____LuaGlobalAccess__LINE__ (ie it doesn't get replaced).

So, is there a way to somehow obtain an unique string, or any other workaround?

PS: Yes I know names that begin with _ are reserved; I use them anyway for purposes like this being careful to pick names that the standard library is extremely unlikely to ever use. Additionally they are in a namespace.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156
  • 1
    Note - if the identifier that begins with an underscore is in a namespace, it's only reserved if it's followed by an uppercase letter. Otherwise, you're perfectly fine to use it. So change the '_Lua' bits to '_lua' and no one can complain. At least about those (the names containing "_____" are a different story). – Michael Burr Nov 23 '09 at 04:58
  • 1
    not sure I get you.. why don't you concat the `lua_name` instead of the `c_name`? Isn't that more likely to be unique? – int3 Nov 23 '09 at 04:59
  • 1
    anyway, I believe http://stackoverflow.com/questions/1489932/c-preprocessor-and-concatenation/1489985#1489985 solves your problem of 'proper substitution'. – int3 Nov 23 '09 at 05:10
  • @splicer: I can't because it's a string. You can go from a hi to "hi" with #hi, but you can't go from "hi" to hi. – Andreas Bonini Nov 23 '09 at 05:11
  • What about names the contain double underscore? Do you know the rules about those? And by careful you mean that you have grepped all the UNIX sources past present and future? If you know it breaks the rules why use it? – Martin York Nov 23 '09 at 05:24

1 Answers1

7

You need to add an extra layer of macros to make the preprocessor do the right thing:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)

#define LUA_GLOBAL(lua_name, c_name) ... TOKENPASTE2(_luaGlobal, __LINE__) ...

Some compilers also support the __COUNTER__ macro, which expands to a new, unique integer every time it is evaluated, so you can use that in place of __LINE__ to generate unique identifiers. I'm not sure if it's valid ISO C, although gcc accepts its use with the -ansi -pedantic options.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • Thanks, __COUNTER__ with the double macro thingy seems to be the best solution. I googled and it's not standard, but it's supported by both gcc and the microsoft's compiler, and that's all my application is compatible with. – Andreas Bonini Nov 23 '09 at 05:18