I wrote some Lua bindings for FTGL's C API. This works well enough, but I ended up with lots of macros, one for each Lua function signature. For example, LUD_NUMBER_NUMBER_TO_NUMBER creates a Lua function that takes a lightuserdata argument followed by two numeric arguments and returns a number.
I like having most of the functions created with macros, because when reading the code, you can tell those functions are simply passing the same values through to the C API, and the functions that are actually written out have more interesting stuff going on.
I'm wondering if there's a way to reduce the redundancy in the macros themselves, though, or a better way to handle this. I thought about having one macro for each number of parameters, and passing in the parameter types (number, userdata, etc.), but you'd have to pass each type name once in lower case and once in upper case, making it difficult to read.
What can I do to clean this up?
#include <stdlib.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <FTGL/ftgl.h>
#define ustring const unsigned char *
/* work around typo in FTGL */
#ifndef ftglGetLayoutAlignment
#define ftglGetLayoutAlignment ftglGetLayoutAlignement
#endif
#define LUD_LUD_TO_NIL(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TLIGHTUSERDATA); \
name(lua_touserdata(L, 1), lua_touserdata(L, 2)); \
return 0; \
}
#define LUD_NUMBER_NUMBER_TO_NIL(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TNUMBER); \
luaL_checktype(L, 3, LUA_TNUMBER); \
name(lua_touserdata(L, 1), lua_tonumber(L, 2), lua_tonumber(L, 3)); \
return 0; \
}
#define LUD_NUMBER_NUMBER_TO_NUMBER(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TNUMBER); \
luaL_checktype(L, 3, LUA_TNUMBER); \
lua_pushnumber(L, name(lua_touserdata(L, 1), lua_tonumber(L, 2), \
lua_tonumber(L, 3))); \
return 1; \
}
#define LUD_NUMBER_TO_NIL(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TNUMBER); \
name(lua_touserdata(L, 1), lua_tonumber(L, 2)); \
return 0; \
}
#define LUD_NUMBER_TO_NUMBER(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TNUMBER); \
lua_pushnumber(L, name(lua_touserdata(L, 1), lua_tonumber(L, 2))); \
return 1; \
}
#define LUD_STRING_NUMBER_TO_NIL(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TSTRING); \
luaL_checktype(L, 3, LUA_TNUMBER); \
name(lua_touserdata(L, 1), lua_tostring(L, 2), lua_tonumber(L, 3)); \
return 0; \
}
#define LUD_STRING_TO_NUMBER(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
luaL_checktype(L, 2, LUA_TSTRING); \
lua_pushnumber(L, name(lua_touserdata(L, 1), lua_tostring(L, 2))); \
return 1; \
}
#define LUD_TO_LUD(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
lua_pushlightuserdata(L, name(lua_touserdata(L, 1))); \
return 1; \
}
#define LUD_TO_NIL(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
name(lua_touserdata(L, 1)); \
return 0; \
}
#define LUD_TO_NUMBER(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); \
lua_pushnumber(L, name(lua_touserdata(L, 1))); \
return 1; \
}
#define NIL_TO_LUD(name) \
int lf_ ## name (lua_State *L) { \
lua_pushlightuserdata(L, name()); \
return 1; \
}
#define STRING_TO_LUD(name) \
int lf_ ## name (lua_State *L) { \
luaL_checktype(L, 1, LUA_TSTRING); \
const char *a1 = lua_tostring(L, 1); \
lua_pushlightuserdata(L, name(a1)); \
return 1; \
}
#define PREFIXED_CONST(name, prefix) \
lua_pushstring(L, #name); \
lua_pushnumber(L, prefix ## _ ## name); \
lua_rawset(L, -3);
#define FT_CONST(name) PREFIXED_CONST(name, FT)
#define FTGL_CONST(name) PREFIXED_CONST(name, FTGL)
LUD_TO_NIL(ftglDestroyFont)
LUD_STRING_TO_NUMBER(ftglAttachFile)
/*
int ftglAttachData(FTGLfont* font, const unsigned char * data,
size_t size);
*/
int lf_ftglAttachData(lua_State *L)
{
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
luaL_checktype(L, 2, LUA_TSTRING);
size_t size;
ustring data = (ustring)lua_tolstring(L, 2, &size);
lua_pushnumber(L, ftglAttachData(
lua_touserdata(L, 1), data, size));
return 1;
}
LUD_NUMBER_TO_NUMBER(ftglSetFontCharMap)
LUD_TO_NUMBER(ftglGetFontCharMapCount)
/*
FT_Encoding* ftglGetFontCharMapList(FTGLfont* font)
*/
int lf_ftglGetFontCharMapList(lua_State *L)
{
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
FTGLfont *font;
FT_Encoding *charMapList;
font = lua_touserdata(L, 1);
unsigned int charMapCount = ftglGetFontCharMapCount(font);
charMapList = ftglGetFontCharMapList(font);
lua_newtable(L);
for (int i = 0; i < charMapCount; i++) {
lua_pushnumber(L, charMapList[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
LUD_NUMBER_NUMBER_TO_NUMBER(ftglSetFontFaceSize)
LUD_TO_NUMBER(ftglGetFontFaceSize)
LUD_NUMBER_TO_NIL(ftglSetFontDepth)
LUD_NUMBER_NUMBER_TO_NIL(ftglSetFontOutset)
LUD_NUMBER_TO_NIL(ftglSetFontDisplayList)
LUD_TO_NUMBER(ftglGetFontAscender)
LUD_TO_NUMBER(ftglGetFontDescender)
LUD_TO_NUMBER(ftglGetFontLineHeight)
/*
void ftglGetFontBBox(FTGLfont* font, const char *string,
int len, float bounds[6]);
*/
int lf_ftglGetFontBBox(lua_State *L)
{
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
luaL_checktype(L, 2, LUA_TSTRING);
size_t len;
const char *a2 = lua_tolstring(L, 2, &len);
float bounds[6];
ftglGetFontBBox(lua_touserdata(L, 1), a2, len, bounds);
lua_newtable(L);
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, bounds[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
LUD_STRING_TO_NUMBER(ftglGetFontAdvance)
LUD_STRING_NUMBER_TO_NIL(ftglRenderFont)
LUD_TO_NUMBER(ftglGetFontError)
STRING_TO_LUD(ftglCreateBitmapFont)
STRING_TO_LUD(ftglCreateBufferFont)
STRING_TO_LUD(ftglCreateExtrudeFont)
STRING_TO_LUD(ftglCreateOutlineFont)
STRING_TO_LUD(ftglCreatePixmapFont)
STRING_TO_LUD(ftglCreatePolygonFont)
STRING_TO_LUD(ftglCreateTextureFont)
LUD_TO_NIL(ftglDestroyLayout)
/*
void ftglGetLayoutBBox(FTGLlayout *layout, const char* string,
float bounds[6]);
*/
int lf_ftglGetLayoutBBox(lua_State *L)
{
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
luaL_checktype(L, 2, LUA_TSTRING);
FTGLlayout *a1 = lua_touserdata(L, 1);
const char *a2 = lua_tostring(L, 2);
float bounds[6];
ftglGetLayoutBBox(a1, a2, bounds);
lua_newtable(L);
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, bounds[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
LUD_STRING_NUMBER_TO_NIL(ftglRenderLayout)
LUD_TO_NUMBER(ftglGetLayoutError)
NIL_TO_LUD(ftglCreateSimpleLayout)
LUD_LUD_TO_NIL(ftglSetLayoutFont)
LUD_TO_LUD(ftglGetLayoutFont)
LUD_NUMBER_TO_NIL(ftglSetLayoutLineLength)
LUD_TO_NUMBER(ftglGetLayoutLineLength)
LUD_NUMBER_TO_NIL(ftglSetLayoutAlignment)
LUD_TO_NUMBER(ftglGetLayoutAlignment)
LUD_NUMBER_TO_NIL(ftglSetLayoutLineSpacing)
/* http://sourceforge.net/p/ftgl/bugs/35/
LUD_TO_NUMBER(ftglGetLayoutLineSpacing) */
int luaopen_luaftgl(lua_State *L)
{
const luaL_Reg api[] = {
/* FTFont functions (FTGL/FTFont.h) */
{ "destroyFont", lf_ftglDestroyFont },
{ "attachFile", lf_ftglAttachFile },
{ "attachData", lf_ftglAttachData },
{ "setFontCharMap", lf_ftglSetFontCharMap },
{ "getFontCharMapCount", lf_ftglGetFontCharMapCount },
{ "getFontCharMapList", lf_ftglGetFontCharMapList },
{ "setFontFaceSize", lf_ftglSetFontFaceSize },
{ "getFontFaceSize", lf_ftglGetFontFaceSize },
{ "setFontDepth", lf_ftglSetFontDepth },
{ "setFontOutset", lf_ftglSetFontOutset },
{ "setFontDisplayList", lf_ftglSetFontDisplayList },
{ "getFontAscender", lf_ftglGetFontAscender },
{ "getFontDescender", lf_ftglGetFontDescender },
{ "getFontLineHeight", lf_ftglGetFontLineHeight },
{ "getFontBoundingBox", lf_ftglGetFontBBox }, /* added */
{ "getFontBBox", lf_ftglGetFontBBox }, /* original */
{ "getFontAdvance", lf_ftglGetFontAdvance },
{ "renderFont", lf_ftglRenderFont },
{ "getFontError", lf_ftglGetFontError },
/* FTGL*Font functions (FTGL/FTGL*Font.h) */
{ "createBitmapFont", lf_ftglCreateBitmapFont },
{ "createBufferFont", lf_ftglCreateBufferFont },
{ "createExtrudeFont", lf_ftglCreateExtrudeFont },
{ "createOutlineFont", lf_ftglCreateOutlineFont },
{ "createPixmapFont", lf_ftglCreatePixmapFont },
{ "createPolygonFont", lf_ftglCreatePolygonFont },
{ "createTextureFont", lf_ftglCreateTextureFont },
/* FTLayout functions (FTGL/FTLayout.h) */
{ "destroyLayout", lf_ftglDestroyLayout },
{ "getLayoutBoundingBox", lf_ftglGetLayoutBBox }, /* added */
{ "getLayoutBBox", lf_ftglGetLayoutBBox }, /* original */
{ "renderLayout", lf_ftglRenderLayout },
{ "getLayoutError", lf_ftglGetLayoutError },
/* FTSimpleLayout functions (FTGL/FTSimpleLayout.h) */
{ "createSimpleLayout", lf_ftglCreateSimpleLayout },
{ "setLayoutFont", lf_ftglSetLayoutFont },
{ "getLayoutFont", lf_ftglGetLayoutFont },
{ "setLayoutLineLength", lf_ftglSetLayoutLineLength },
{ "getLayoutLineLength", lf_ftglGetLayoutLineLength },
{ "setLayoutAlignment", lf_ftglSetLayoutAlignment },
{ "getLayoutAlignment", lf_ftglGetLayoutAlignment }, /* added */
{ "getLayoutAlignement", lf_ftglGetLayoutAlignment }, /* original */
{ "setLayoutLineSpacing", lf_ftglSetLayoutLineSpacing },
/* http://sourceforge.net/p/ftgl/bugs/35/
{ "getLayoutLineSpacing", lf_ftglGetLayoutLineSpacing }, */
{ NULL, NULL }
};
#if LUA_VERSION_NUM == 501
luaL_register (L, "luaftgl", api);
#else
luaL_newlib (L, api);
#endif
/* FT_Encoding (freetype.h) */
FT_CONST(ENCODING_NONE)
FT_CONST(ENCODING_MS_SYMBOL)
FT_CONST(ENCODING_UNICODE)
FT_CONST(ENCODING_SJIS)
FT_CONST(ENCODING_GB2312)
FT_CONST(ENCODING_BIG5)
FT_CONST(ENCODING_WANSUNG)
FT_CONST(ENCODING_JOHAB)
FT_CONST(ENCODING_MS_SJIS)
FT_CONST(ENCODING_MS_GB2312)
FT_CONST(ENCODING_MS_BIG5)
FT_CONST(ENCODING_MS_WANSUNG)
FT_CONST(ENCODING_MS_JOHAB)
FT_CONST(ENCODING_ADOBE_STANDARD)
FT_CONST(ENCODING_ADOBE_EXPERT)
FT_CONST(ENCODING_ADOBE_CUSTOM)
FT_CONST(ENCODING_ADOBE_LATIN_1)
FT_CONST(ENCODING_OLD_LATIN_2)
FT_CONST(ENCODING_APPLE_ROMAN)
/* FTGL constants */
FTGL_CONST(RENDER_FRONT)
FTGL_CONST(RENDER_BACK)
FTGL_CONST(RENDER_SIDE)
FTGL_CONST(RENDER_ALL)
FTGL_CONST(ALIGN_LEFT)
FTGL_CONST(ALIGN_CENTER)
FTGL_CONST(ALIGN_RIGHT)
FTGL_CONST(ALIGN_JUSTIFY)
return 1;
}
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
\$\endgroup\$