Erik McClure

Migrating To A Static Blog


I’ve finished constructing a new personal website for myself using hugo, and I’m moving my blog over there so I have more control over what gets loaded, and more importantly, so the page doesn’t attempt to load Blogger’s 5 MB worth of bloated javascript nonsense just to read some text. It also fixes math and code highlighting while reading on mobile. If you reached this post using Blogger, you’ll be redirected or will soon be redirected to the corresponding post on my new website.

All comments have been preserved from the original posts, but making new comments is currently disabled - I haven’t decided if I want to use Disqus or attempt something else. An RSS feed is available on the bottom of the page for tracking new posts that should mimic the Blogger RSS feed, if you were using that. If something doesn’t work, poke me on twitter and I’ll try to fix it.

I implemented share buttons with simple links, without embedding any crazy javascript bullshit. In fact, the only external resource loaded is a Google tracking ID for pageviews. Cloudflare is used to enforce an HTTPS connection over the custom domain even though the website is hosted on Github Pages.

Hopefully, the new font and layout is easier to read than Blogger’s tiny text and bullshit theme nonsense.


How To Avoid Memorizing Times Tables


I was recently told that my niece was trying to memorize her times tables. As an applied mathematician whose coding involves plenty of multiplication, I was not happy to hear this. Nobody who does math actually memorizes times tables, and furthermore, forcing a child to memorize anything is probably the worst possible thing you can do in modern society. No one should memorize their times tables, they should learn how to calculate them. Forcing children to memorize useless equations for no reason is a great way to either ensure they hate math, teach them they should blindly memorize and believe anything adults tell them, or both. So for any parents who wish to teach their children how to be critical thinkers and give them an advantage on their next math test, I am going to describe how to derive the entire times tables with only 12 rules.

  1. Anything multiplied by 1 is itself. Note that I said anything, that includes fractions, pies, cars, the moon, or anything else you can think of. Multiplying it by 1 just gives you back the same result.

  2. Any number multiplied by 10 has a zero added on the end. 1 becomes 10, 2 becomes 20, 72 becomes 720, 9999 becomes 99990, etc.

  3. Any single digit multiplied by 11 simply adds itself on the end instead of 0. 1 becomes 11, 2 becomes 22, 5 becomes 55, etc. This is because you never need to multiply something by eleven. Instead, multiply it by 10 (add a zero to it) then add itself.

    \[ \begin{aligned} 11*11 = 11*(10 + 1) = 11*10 + 11 = 110 + 11 = 121\\ 12*11 = 12*(10 + 1) = 12*10 + 12 = 120 + 12 = 132 \end{aligned} \]

  4. You can always reverse the numbers being multiplied and the same result comes out. $$ 12*2 = 2*12 $$, $$ 8*7 = 7*8 $$, etc. This is a simple rule, but it’s very easy to forget, so keep it in mind.

  5. Anything multiplied by 2 is doubled, or added to itself, but you only need to do this up to 9. For example, $$ 4*2 = 4 + 4 = 8 $$. Alternatively, you can count up by 2 that many times:

    \[ 4*2 = 2 + 2 + 2 + 2 = 4 + 2 + 2 = 6 + 2 = 8 \]
    To multiply any large number by two, double each individual digit and carry the result. Because you multiply each digit by 2 separately, the highest result you can get from this is 18, so you will only ever carry a 1, just like in addition.
    \[ \begin{aligned} \begin{matrix} 3 & 6\\ & 2\\ \hline & \\ & \\ \hline & \end{matrix}\quad \begin{matrix} 3 & 6\\ & 2\\ \hline 1 & 2\\ & \\ \hline & \end{matrix}\quad \begin{matrix} 3 & 6\\ & 2\\ \hline 1 & 2\\ 6 & \\ \hline & \end{matrix}\quad \begin{matrix} 3 & 6\\ & 2\\ \hline 1 & 2\\ 6 & \\ \hline 7 & 2 \end{matrix} \end{aligned} \]
    This method is why multiplying anything by 2 is one of the easiest operations in math, and as a result the rest of our times table rules are going to rely heavily on it. Don’t worry about memorizing these results - you’ll memorize them whether you want to or not simply because of how often you use them.

  6. Any number multiplied by 3 is multiplied by 2 and then added to itself. For example:

    \[ 6*3 = 6*(2 + 1) = 6*2 + 6 = 12 + 6 = 18 \]
    Alternatively, you can add the number to itself 3 times: $$ 3*3 = 3 + 3 + 3 = 6 + 3 = 9 $$

  7. Any number multiplied by 4 is simply multiplied by 2 twice. For example: $$ 7*4 = 7*2*2 = 14*2 = 28 $$

  8. Any number multiplied by 5 is the same number multiplied by 4 and then added to itself.

    \[ 6*5 = 6*(4 + 1) = 6*4 + 6 = 6*2*2 + 6 = 12*2 + 6 = 24 + 6 = 30 \]
    Note that I used our rule for 4 here to break it up and calculate it using only 2. Once kids learn division, they will notice that it is often easier to calculate 5 by multiplying by 10 and halving the result, but we assume no knowledge of division.

  9. Any number multiplied by 8 is multiplied by 4 and then by 2, which means it’s actually just multiplied by 2 three times. For example: $$ 7*8 = 7*4*2 = 7*2*2*2 = 14*2*2 = 28*2 = 56 $$

  10. Never multiply anything by 12. Instead, multiply it by 10, then add itself multiplied by 2. For example: $$ 12*12 = 12*(10 + 2) = 12*10 + 12*2 = 120 + 24 = 144 $$

  11. Multiplying any single digit number by 9 results in a number whose digits always add up to nine, and whose digits decrease in the right column while increasing in the left column.

    \[ \begin{aligned} 9 * 1 = 09\\ 9 * 2 = 18\\ 9 * 3 = 27\\ 9 * 4 = 36\\ 9 * 5 = 45\\ 9 * 6 = 54\\ 9 * 7 = 63\\ 9 * 8 = 72\\ 9 * 9 = 81 \end{aligned} \]
    10, 11, and 12 can be calculated using rules for those numbers.

  12. For both 6 and 7, we already have rules for all the other numbers, so you just need to memorize 3 results:

    \[ \begin{aligned} 6*6 = 36\\ 6*7 = 42\\ 7*7 = 49 \end{aligned} \]
    Note that $$ 7*6 = 6*7 = 42 $$. This is where people often forget about being able to reverse the numbers. Every single other multiplication involving 7 or 6 can be calculated using a rule for another number.

And there you have it. Instead of trying to memorize a bunch of numbers, kids can learn rules that build on top of each other, each taking advantage of the rules established before it. It’s much more engaging then trying to memorize a giant table of meaningless numbers, a task that’s so mind-numbingly boring I can’t imagine forcing an adult to do it, let alone a small child. More importantly, this task teaches you what math is really about. It’s not about numbers, or adding things together, or memorizing a bunch of formulas. It’s establishing simple rules, and then combining those rules together into more complex rules you can use to solve more complex problems.

This also establishes a fundamental connection to computer science that is often glossed over. Both math and programming are repeated abstraction and generalization. It’s about combining simple rules into a more generalized rule, which can then be abstracted into a simpler form and combined to create even more complex rules. Programs start with machine instructions, while math starts with propositions. Programs have functions, and math has theorems. Both build on top of previous results to create more powerful and expressive tools. Both require a spark of creativity to recognize similarities between seemingly unrelated concepts and unite them in a more generalized framework.

We can demonstrate all of this simply by refusing to memorize our times tables.


Ignoring Outliers Creates Racist Algorithms


Have you built an algorithm that mostly works? Does it account for almost everyone’s needs, save for a few weird outliers that you ignore because they make up 0.0001% of the population? Congratulations, your algorithm is racist! To illustrate how this happens, let’s take a recent example from Facebook. My friend’s message was removed for “violating community standards”. Now, my friend has had all sorts of ridiculous problems with Facebook, so to test my theory, I posted the exact same message on my page, and then had him report it.

Golly gee, look at that, Facebook confirmed the message I sent does not violate community guidelines, but he’s still banned for 12 hours for posting the exact same thing. What I suspect happened is this: Facebook has gotten mad at my friend for having a weird name multiple times, but he can’t prove what his name is because he doesn’t have access to his birth certificate because of family problems, and he thinks someone’s been falsely reporting a bunch of his messages. The algorithm for determining whether or not something is “bad” probably took these misleading inputs, combined it with a short list of so-called “dangerous” topics like “terrorism”, and then decided that if anyone reported one of his messages, it was probably bad. On the other hand, I have a very western name and nobody reports anything I post, so either the report actually made it to a human being, or the algorithm simply decided it was probably fine.

Of course, the algorithm was wrong about my friend’s message. But Facebook doesn’t care. I’m sure a bunch of self-important programmers are just itching to tell me we can’t deal with all the edge-cases in a commercial algorithm because it’s infeasible to account for all of them. What I want to know is, have any of these engineers ever thought about who the edge-cases are? Have they ever thought about the kind of people who can’t produce birth certificates, or don’t have a driver’s license, or have strange names that don’t map to unicode properly because they aren’t western enough?

Poor people. Minorities. Immigrants. Disabled people. All these people they claim to care about, all this talk of diversity and equal opportunity and inclusive policies, and they’re building algorithms that by their very nature will exclude those less fortunate than them. Facebook’s algorithm probably doesn’t even know that my friend is asian, yet it’s still discriminating against him. Do you know who can follow all those rules and assumptions they make about normal people? Rich people. White people. Privileged people. These algorithms benefit those who don’t need help, and disproportionately punish those who don’t need any more problems.

What’s truly terrifying is that Silicon Valley wants to run the world, and it wants to automate everything using a bunch of inherently flawed algorithms. Algorithms that might be impossible to perfect, given the almost unlimited number of edge-cases that reality can come up with. In fact, as I am writing this article, Chrome doesn’t recognize “outlier” as a word, even though Google itself does.

Of course, despite this, Facebook already built an algorithm that tries to detect “toxicity” and silences “unacceptable” opinions. Even if they could build a perfect algorithm for detecting “bad speech”, do these companies really think forcibly restricting free speech will accomplish anything other than improving their own self-image? A deeply cynical part of me thinks the only thing these companies actually care about is looking good. A slightly more optimistic part of me thinks a bunch of well-meaning engineers are simply being stupid.

You can’t change someone’s mind by punching them in the face. Punching people in the face may shut them up, but it does not change their opinion. It doesn’t fix anything. Talking to them does. I’m tired of this industry hiding problems behind shiny exteriors instead of fixing them. That’s what used car salesmen do, not engineers. Programming has devolved into an art of deceit, where coders hide behind pretty animations and huge frameworks that sweep all their problems under the rug, while simultaneously screwing over the people who were supposed to benefit from an “egalitarian” industry that seems less and less egalitarian by the day.

Either silicon valley needs to start dealing with people that don’t fit in neat little boxes, or it will no longer be able to push humanity forward. If we’re going to move forward as a species, we have to do it together. Launching a bunch of rich people into space doesn’t accomplish anything. Curing cancer for rich people doesn’t accomplish anything. Inventing immortality for rich people doesn’t accomplish anything. If we’re going to push humanity forward, we have to push everyone forward, and that means dealing with all 7 billion outliers.

I hope silicon valley doesn’t drag us back to the feudal age, but I’m beginning to think it already has.


I Used To Want To Work For Google


A long time ago I thought Google was this magical company that truly cared about engineering and solving problems instead of maximizing shareholder value. Then Larry Page became CEO and I realized they were not a magical unicorn and lamented the fact that they had been transformed into “just another large company”. Several important things happened between that post and now: Microsoft got a new CEO, so I decided to give them a shot and got hired there. I quit right before Windows 10 came out because I knew it was going to be a disaster. More recently, it’s become apparent that Google had gone far past simply being a behemoth unconcerned with the cries of the helpless and transformed into something outright malevolent. It’s silenced multiple reporters, blocked windows phone from accessing youtube out of spite, and successfully gotten an entire group of researchers fired by threatening to pull funding (but that didn’t stop them).

This is evil. This is horrifying. This is the kind of stuff Microsoft did in the 90s that made everyone hate it so much they still have to fight against the repercussions of decisions made two decades ago because of the sheer amount of damage they did and lives they ruined. I’m at the point where I’d rather go back to Microsoft, whose primary sin at this point is mostly just being incompetent instead of outright evil, rather than Google, who is actually doing things that are fundamentally morally wrong. These are the kinds of decisions that are bald-faced abuses of power, without any possible “good intention” driving them. It’s vile. There is no excuse.

As an ex-Microsoft employee, I can assure you that at no point did I think Microsoft was doing something evil while I was there. I haven’t seen Microsoft do anything outright evil since I left, either. The few times they came close they backed off and apologized later. Microsoft didn’t piss people off by being evil, it pissed people off by being dumb. I was approached by a Google recruiter shortly after I left and I briefly considered going to Google because I considered them vastly more competent, and I still do. However, no amount of engineering competency can make me want to work for a company that actively does things I consider morally reprehensible. This is the same reason I will never work for Facebook. I’ve drawn a line in the sand, and I find myself in the surprising situation of being on the opposite side of Google, and discovering that Microsoft, of all companies, isn’t with them.

I always thought I’d be able to mostly disregard the questionable things that Google and Microsoft were doing and compare them purely on the competency of their engineers. However, it seems that Google has every intention of driving me away by doing things so utterly disgusting I could never work there and still be able to sleep at night. This worries me deeply, because as these companies get larger and larger, they eat up all the other sources of employment. Working at a startup that isn’t one of the big 5 won’t help if it gets bought out next month. One friend of mine with whom I shared many horror stories with worked at LinkedIn. He was not happy when he woke up one day to discover he now worked for the very company he had heard me complaining about. Even now, he’s thinking of quitting, and not because Microsoft is evil - they’re just so goddamn dumb.

The problem is that there aren’t many other options, short of starting your own company. Google is evil, Facebook is evil, Apple is evil if you care about open hardware, Microsoft is too stupid to be evil but might at some point become evil again, and Amazon is probably evil and may or may not treat it’s employees like shit depending on who you ask. Even if you don’t work directly for them, you’re probably using their products or services. At some point, you have to put food on the table. This is why I generally refuse to blame someone for working for an evil company because the economy punishes you for trying to stand up for your morals. It’s not the workers fault, here, it’s Wall Street incentivizing rotten behavior by rewarding short-term profits instead of long-term growth. A free market optimizes to a monopoly. Monopolies are bad. I don’t know what people don’t get about this. We’re fighting over stupid shit like transgender troops or gay rights instead of just treating other human beings with decency, all the while letting rich people rob us blind as they decimate the economy. This is stupid. I would daresay it’s almost more stupid than the guy at Microsoft who decided to fire all the testers.

But I guess I’ll take unrelenting stupidity over retaliating against researchers for criticizing you. At least until Microsoft remembers how to be evil. Then I don’t know what I’ll do.

I don’t know what anyone will do.


Integrating LuaJIT and Autogenerating C Bindings In Visual Studio


Lua is a popular scripting language due to its tight integration with C. LuaJIT is an extremely fast JIT compiler for Lua that can be integrated into your game, which also provides an FFI Library that directly interfaces with C functions, eliminating most overhead. However, the FFI library only accepts a subset of the C standard. Specifically, “C declarations are not passed through a C pre-processor, yet. No pre-processor tokens are allowed, except for #pragma pack.” The website suggests running the header file through a preprocesser stage, but I have yet to find a LuaJIT tutorial that actually explains how to do this. Instead, all the examples simply copy+paste the function prototype into the Lua file itself. Doing this with makefiles and GCC is trivial, because you just have to add a compile step using the -E option, but integrating this with Visual Studio is more difficult. In addition, I’ll show you how to properly load scripts and modify the PATH lookup variable so your game can have a proper scripts folder instead of dumping everything in bin.

Compilation

To begin, we need to download LuaJIT and get it to actually compile. Doing this manually isn’t too difficult, simply open an x64 Native Tools Command Prompt (or x86 Native Tools if you want 32-bit), navigate to src/msvcbuild.bat and run msvcbuild.bat. The default options will build an x64 or x86 dll with dynamic linking to the CRT. If you want a static lib file, you need to run it with the static option. If you want static linking to the CRT so you don’t have to deal with that annoying Visual Studio Runtime Library crap, you’ll have to modify the .bat file directly. Specifically, you need to find %LJCOMPILE% /MD and change it to %LJCOMPILE% /MT. This will then compile the static lib or dll with static CRT linking to match your other projects.

This is a bit of a pain, and recently I’ve been trying to automate my build process and dependencies using vcpkg to act as a C++ package manager. A port of LuaJIT is included in the latest update of vcpkg, but if you want one that always statically links to the CRT, you can get it here.

An important note: the build instructions for LuaJIT state that you should copy the lua scripts contained in src/jit to your application folder. What it doesn’t mention is that this is optional - those scripts contain debugging instructions for the JIT engine, which you probably don’t need. It will work just fine without them.

Once you have LuaJIT built, you should add it’s library file to your project. This library file is called lua51.lib (and the dll is lua51.dll), because LuaJIT is designed as a drop-in replacement for the default Lua runtime. Now we need to actually load Lua in our program and integrate it with our code. To do this, use lua_open(), which returns a lua_State* pointer. You will need that lua_State* pointer for everything else you do, so store it somewhere easy to get to. If you are building a game using an Entity Component System, it makes sense to build a LuaSystem that stores your lua_State* pointer.

Initialization

The next step is to load in all the standard Lua libraries using luaL_openlibs(L). Normally, you shouldn’t do this if you need script sandboxing for player-created scripts. However, LuaJIT’s FFI library is inherently unsafe. Any script with access to the FFI library can call any kernel API it wants, so you should be extremely careful about using LuaJIT if this is a use-case for your game. We can also register any C functions we want to the old-fashioned way via lua_register, but this is only useful for functions that don’t have C analogues (due to having multiple return values, etc).

There is one function in particular that you probably want to overload, and that is the print() function. By default, Lua will simply print to standard out, but if you aren’t redirecting standard out to your in-game console, you probably have your own std::ostream (or even a custom stream class) that is sent all log messages. By overloading print(), we can have our Lua scripts automatically write to both our log file and our in-game console, which is extremely useful. Here is a complete re-implementation of print that outputs to an arbitrary std::ostream& object:

int PrintOut(lua_State *L, std::ostream& out)
{
  int n = lua_gettop(L);  /* number of arguments */
  if(!n)
    return 0;
  int i;
  lua_getglobal(L, "tostring");
  for(i = 1; i <= n; i++)
  {
    const char *s;
    lua_pushvalue(L, -1);  /* function to be called */
    lua_pushvalue(L, i);   /* value to print */
    lua_call(L, 1, 1);
    s = lua_tostring(L, -1);  /* get result */
    if(s == NULL)
      return luaL_error(L, LUA_QL("tostring") " must return a string to "
        LUA_QL("print"));
    if(i > 1) out << "\t";
    out << s;
    lua_pop(L, 1);  /* pop result */
  }
  out << std::endl;
  return 0;
}
To overwrite the existing print function, we need to first define a Lua compatible shim function. In this example, I pass std::cout as the target stream:

int lua_Print(lua_State *L)
{
  return PrintOut(L, std::cout);
}
Now we simply register our lua_Print function using lua_register(L, "print", &lua_Print). If we were doing this in a LuaSystem object, our constructor would look like this:

LuaSystem::LuaSystem()
{
  L = lua_open();
  luaL_openlibs(L);
  lua_register(L, "print", &lua_Print);
}
To clean up our Lua instance, we need to both trigger a final GC iteration to clean up any dangling memory, and then we call lua_close(L), so our destructor would look like this:
LuaSystem::~LuaSystem()
{
  lua_gc(L, LUA_GCCOLLECT, 0);
  lua_close(L);
  L = 0;
}

Loadings Scripts via Require

At this point most tutorials skip to the part where you load a Lua script and write “Hello World”, but we aren’t done yet. Integrating Lua into your game means loading scripts and/or arbitrary strings as Lua code while properly resolving dependencies. If you don’t do this, any one of your scripts that relies on another script will have to do require("full/path/to/script.lua"). We also face another problem - if we want to have a scripts folder where we simply automatically load every single script into our workspace, simply loading them all can cause duplicated code, because luaL_loadfile does not have any knowledge of require. You can solve this by simply loading a single bootstrap.lua script which then loads all your game’s scripts via require, but we’re going to build a much more robust solution.

First, we need to modify Lua’s PATH variable, or the variable that controls where it looks up scripts relative to our current directory. This function will append a path (which should be of the form "path/to/scripts/?.lua") to the beginning of the PATH variable, giving it highest priority, which you can then use to add as many script directories as you want in your game, and any lua script from any of those folders will then be able to require() a script from any other folder in PATH without a problem. Obviously, you should probably only add one or two folders, because you don’t want to deal with potential name conflicts in your script files.

int AppendPath(lua_State *L, const char* path)
{
  lua_getglobal(L, "package");
  lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1)
  std::string npath = path;
  npath.append(";");
  npath.append(lua_tostring(L, -1)); // grab path string from top of stack
  lua_pop(L, 1);
  lua_pushstring(L, npath.c_str());
  lua_setfield(L, -2, "path"); // set the field "path" in table at -2 with value at top of stack
  lua_pop(L, 1); // get rid of package table from top of stack
  return 0;
}
Next, we need a way to load all of our scripts using require() so that Lua properly resolves the dependencies. To do this, we create a function in C that literally calls the require() function for us:
int Require(lua_State *L,const char *name)
{
  lua_getglobal(L, "require");
  lua_pushstring(L, name);
  int r = lua_pcall(L, 1, 1, 0);
  if(!r)
    lua_pop(L, 1);
  WriteError(L, r, std::cout);
  return r;
}
By using this to load all our scripts, we don’t have to worry about loading them in any particular order - require will ensure everything gets loaded correctly. An important note here is WriteError(), which is a generic error handling function that processes Lua errors and writes them to a log. All errors in lua will return a nonzero error code, and will usually push a string containing the error message to the stack, which must then be popped off, or it’ll mess things up later.
void WriteError(lua_State *L, int r, std::ostream& out)
{
  if(!r)
    return;
  if(!lua_isnil(L, -1)) // Check if a string was pushed
  {
    const char* m = lua_tostring(L, -1);
    out << "Error " << r << ": " << m << std::endl;
    lua_pop(L, 1);
  }
  else
    out << "Error " << r << std::endl;
}

Automatic C Binding Generation

Fantastic, now we’re all set to load up our scripts, but we still need to somehow define a header file and also load that header file into LuaJIT’s FFI library so our scripts have direct access to our program’s exposed C functions. One way to do this is to just copy+paste your C function definitions into a Lua file in your scripts folder that is then automatically loaded. This, however, is a pain in the butt and is error-prone. We want to have a single source of truth for our function definitions, which means defining our entire LuaJIT C API in a single header file, which is then loaded directly into LuaJIT. Predictably, we will accomplish this by abusing the C preprocessor:

#ifndef __LUA_API_H__
#define __LUA_API_H__

#ifndef LUA_EXPORTS
#define LUAFUNC(ret, name, ...) ffi.cdef[[ ret lua_##name(__VA_ARGS__); ]]; name = ffi.C.lua_##name
local ffi = require("ffi")
ffi.cdef[[ // Initial struct definitions
#else
#define LUAFUNC(ret, name, ...) ret __declspec(dllexport) lua_##name(__VA_ARGS__)
extern "C" { // Ensure C linkage is being used
#endif

struct GameInfo
{
  uint64_t DashTail;
  uint64_t MaxDash;
};

typedef const char* CSTRING; // VC++ complains about having const char* in macros, so we typedef it here

#ifndef LUA_EXPORTS
]] // End struct definitions
#endif

  LUAFUNC(CSTRING, GetGameName);
  LUAFUNC(CSTRING, IntToString, int);
  LUAFUNC(void, setdeadzone, float);

#ifdef Everglade_EXPORTS
}
#endif

#endif
The key idea here is to use macros such that, when we pass this through the preprocessor without any predefined constants, it will magically turn into a valid Lua script. However, when we compile it in our C++ project, our project defines LUA_EXPORTS, and the result is a valid C header. Our C LUAFUNC is set up so that we’re using C linkage for our structs and functions, and that we’re exporting the function via __declspec(dllexport). This obviously only works for Visual Studio so you’ll want to set up a macro for the GCC version, but I will warn you that VC++ got really cranky when i tried to use a macro for that in my code, so you may end up having to redefine the entire LUAFUNC macro for each compiler.

At this point, we have a bit of a choice to make. It’s more convenient to have the C functions available in the global namespace, which is what this script does, because this simplifies calling them from an interactive console. However, using ffi.C.FunctionName is significantly faster. Technically the fastest way is declaring local C = ffi.C at the top of a file and then calling the functions via C.FunctionName. Luckily, importing the functions into the global namespace does not preclude us from using the “fast” way of calling them, so our script here imports them into the global namespace for ease of use, but in our scripts we can use the C.FunctionName method instead. Thus, when outputting our Lua script, our LUAFUNC macro wraps our function definition in a LuaJIT ffi.cdef block, and then runs a second Lua statement that brings the function into the global namespace. This is why we have an initial ffi.cdef code block for the structs up top, so we can include that second lua statement after each function definition.

Now we need to set up our compilation so that Visual Studio generates this file without any predefined constants and outputs the resulting lua script to our scripts folder, where our other in-game scripts can automatically load it from. We can accomplish this using a Post-Build Event (under Configuration Properties -> Build Events -> Post-Build Event), which then runs the following code:

CL LuaAPI.h /P /EP /u
COPY "LuaAPI.i" "../bin/your/script/folder/LuaAPI.lua" /Y
Visual Studio can sometimes be finicky about that newline, but if you put in two statements on two separate lines, it should run both commands sequentially. You may have to edit the project file directly to convince it to actually do this. The key line here is CL LuaAPI.h /P /EP /u, which tells the compiler to preprocess the file and output it to a *.i file. There is no option to configure the output file, it will always be the exact same file but with a .i extension, so we have to copy and rename it ourselves to our scripts folder using the COPY command.

Loading and Calling Lua Code

We are now set to load all our lua scripts in our script folder via Require, but what if we want an interactive Lua console? There are lua functions that read strings, but to make this simpler, I will provide a function that loads a lua script from an arbitrary std::istream and outputs to an arbitrary std::ostream:

const char* _luaStreamReader(lua_State *L, void *data, size_t *size)
{
  static char buf[CHUNKSIZE];
  reinterpret_cast<std::istream*>(data)->read(buf, CHUNKSIZE);
  *size = reinterpret_cast<std::istream*>(data)->gcount();
  return buf;
}

int Load(lua_State *L, std::istream& s, std::ostream& out)
{
  int r = lua_load(L, &_luaStreamReader, &s, 0);

  if(!r)
  {
    r = lua_pcall(L, 0, LUA_MULTRET, 0);
    if(!r)
      PrintOut(L, out);
  }

  WriteError(L, r, out);
  return r;
}
Of course, the other question is how to call Lua functions from our C++ code directly. There are many, many different implementations of this available, of varying amounts of safety and completeness, but to get you started, here is a very simple implementation in C++ using templates. Note that this does not handle errors - you can change it to use lua_pcall and check the return code, but handling arbitrary Lua errors is nontrivial.
template<class T, int N>
struct LuaStack;

template<class T> // Integers
struct LuaStack<T, 1>
{
  static inline void Push(lua_State *L, T i) { lua_pushinteger(L, static_cast<lua_Integer>(i)); }
  static inline T Pop(lua_State *L) { T r = (T)lua_tointeger(L, -1); lua_pop(L, 1); return r; }
};
template<class T> // Pointers
struct LuaStack<T, 2>
{
  static inline void Push(lua_State *L, T p) { lua_pushlightuserdata(L, (void*)p); }
  static inline T Pop(lua_State *L) { T r = (T)lua_touserdata(L, -1); lua_pop(L, 1); return r; }
};
template<class T> // Floats
struct LuaStack<T, 3>
{
  static inline void Push(lua_State *L, T n) { lua_pushnumber(L, static_cast<lua_Number>(n)); }
  static inline T Pop(lua_State *L) { T r = static_cast<T>(lua_touserdata(L, -1)); lua_pop(L, 1); return r; }
};
template<> // Strings
struct LuaStack<std::string, 0>
{
  static inline void Push(lua_State *L, std::string s) { lua_pushlstring(L, s.c_str(), s.size()); }
  static inline std::string Pop(lua_State *L) { size_t sz; const char* s = lua_tolstring(L, -1, &sz); std::string r(s, sz); lua_pop(L, 1); return r; }
};
template<> // Boolean
struct LuaStack<bool, 1>
{
  static inline void Push(lua_State *L, bool b) { lua_pushboolean(L, b); }
  static inline bool Pop(lua_State *L) { bool r = lua_toboolean(L, -1); lua_pop(L, 1); return r; }
};
template<> // Void return type
struct LuaStack<void, 0> { static inline void Pop(lua_State *L) { } };

template<typename T>
struct LS : std::integral_constant<int, 
  std::is_integral<T>::value + 
  (std::is_pointer<T>::value * 2) + 
  (std::is_floating_point<T>::value * 3)>
{};

template<typename R, int N, typename Arg, typename... Args>
inline R _callLua(const char* function, Arg arg, Args... args)
{
  LuaStack<Arg, LS<Arg>::value>::Push(_l, arg);
  return _callLua<R, N, Args...>(function, args...);
}
template<typename R, int N>
inline R _callLua(const char* function)
{
  lua_call(_l, N, std::is_void<R>::value ? 0 : 1);
  return LuaStack<R, LS<R>::value>::Pop(_l);
}

template<typename R, typename... Args>
inline R CallLua(lua_State *L, const char* function, Args... args)
{
  lua_getglobal(L, function);
  return _callLua<R, sizeof...(Args), Args...>(L, function, args...);
}
Now you have everything you need for an extensible Lua scripting implementation for your game engine, and even an interactive Lua console, all using LuaJIT. Good Luck!


Avatar

Archive

  1. 2025
  2. 2024
  3. 2023
  4. 2022
  5. 2021
  6. 2020
  7. 2019
  8. 2018
  9. 2017
  10. 2016
  11. 2015
  12. 2014
  13. 2013
  14. 2012
  15. 2011
  16. 2010
  17. 2009