C++: Generating similar methods with macros I am currently working on a project that involves Lua. For convenience, I created a wrapper class for the Lua state structure. However, I also added some methods for getting globals and table fields. Since these methods are very similar, I decided do turn them into macros.

Do you consider this a good solution? Would you have done it another way?

#include &lt;lua/lua.hpp&gt;

#include &lt;util/Exception.h&gt;
#include "LuaState.h"

namespace gnaar {

LuaState::LuaState() : ls(luaL_newstate())
{
    luaL_openlibs(ls);
}

void LuaState::doFile(const char* file)
{
    if (luaL_dofile(ls, file) != 0)
    {
        throwException(lua_tostring(ls, -1));
    }
}

lua_State* LuaState::operator*()
{
    return ls;
}

LuaState::~LuaState()
{
    lua_close(ls);
}

//==================== ugly macro stuff below, beware ====================

#define _getGlobal(_lua, _type, _check, _conversion)                    \
    template &lt;&gt;                                                         \
    _type LuaState::getGlobal&lt;_type&gt;(const char* name, _type dfault)    \
    {                                                                   \
        lua_getglobal(_lua, name);                                      \
        if (_check(_lua, -1))                                           \
        {                                                               \
            return (_type) _conversion(_lua, -1);                       \
        }                                                               \
        else                                                            \
        {                                                               \
            return dfault;                                              \
        }                                                               \
    }

#define _getTableField(_lua, _type, _check, _conversion)    \
    template &lt;&gt; _type LuaState::getTableField&lt;_type&gt;        \
    (const char* table, const char* key, _type def)         \
    {                                                       \
        lua_getglobal(_lua, table);                         \
        if (!lua_istable(_lua, -1))                         \
        {                                                   \
            return def;                                     \
        }                                                   \
        _type result;                                       \
        lua_pushstring(_lua, key);                          \
        lua_gettable(_lua, -2);                             \
        if (!_check(_lua, -1))                              \
        {                                                   \
            result = def;                                   \
        }                                                   \
        else                                                \
        {                                                   \
            result = (_type) _conversion(_lua, -1);         \
        }                                                   \
        lua_pop(_lua, 1);                                   \
        return result;                                      \
    }

_getGlobal(ls, int, lua_isnumber, lua_tonumber)

_getGlobal(ls, const char*, lua_isstring, lua_tostring)

_getGlobal(ls, bool, lua_isboolean, lua_toboolean)

_getTableField(ls, int, lua_isnumber, lua_tonumber)

_getTableField(ls, const char*, lua_isstring, lua_tostring)

_getTableField(ls, bool, lua_isboolean, lua_toboolean)

} // namespace gnaar
Do you consider this a good solution?


No.


  Would you have done it another way?


Yes.

Macros are never the solution. There is always a better tool. Macros are designed to deal with Hardware/OS/Compiler variations. You should limit their use to this situaation.

It looks like your macros are used to manually insert functions for specific situations. It seems like these could be deduced using template specializations. It is hard to provide a better solution without more code. But let my give it a go (this will probably be wrong as I don't have enough information).

template&lt;typename Type&gt;
TypeOfLuaObject LuaState::lua;

template&lt;typename Type&gt;
struct DoCheck;
template&lt;typename Type&gt;
struct DoConversion;

// Provide a specialization of these two macros for each
// type you are using.
template&lt;&gt;
struct DoCheck&lt;int&gt;
{
    bool operator()(TypeOfLuaObject&amp; lauObjec, int val) const {return lua_isnumber(lauObjec, val);}
};
template&lt;&gt;
struct DoConversion&lt;int&gt;
{
    int operator()(TypeOfLuaObject&amp; lauObjec, int val) const {return lua_tonumber(lauObjec, val);}
};

// The main function can then be left in its generalized form
// It will pick up the specialization of the above macros.
// Type will be deduced automatically from `def` parameter and the other two
// template parameters will be deduced from `Type` so you do not need to manually
// specify the type when calling.
template&lt;typename Type, typename Check=DoCheck&lt;Type&gt;, Conversion=DoConversion&lt;T&gt;&gt;
Type LuaState::getTableField(const char* table, const char* key, Type def)
{
    lua_getglobal(lua, table);
    if (!lua_istable(lua, -1))
    {   
        return def;
    }   
    Type result;
    lua_pushstring(lua, key);
    lua_gettable(lua, -2);
    Check doCheckObj;
    if (!checkObj(lua, -1))
    {   
        result = def;
    }   
    else
    {   
        Conversion doConversionObj;
        result = doConversionObj(lua, -1);
    }   
    lua_pop(lua, 1); 
    return result;
}


Other comments:

When you use macros. Make sure they are all uppercase. 

Macros do not obey scope boundaries. So we reserve all uppercase letters for macros to prevent accidental smashing of normal variables.

Don't use '_' as a prefix for identifiers.
Most people don't know the actual rules for this. So even if you do it will confuse them.

But you are breaking the rule with your macros _getGlobal and _getTableField. As they are macros they are not bound by the scope of your namespace and thus could potentially be trampling on some system macro.

See the artile I wrote on SO: https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier/228797#228797