Register C-function with Lua fails silently, but only with exact name 'add_key'

, , ,

Registering a function in C to call from Lua via playdate->lua-> addFunction, fails silently (so without an error) if the name of that function (in C, not the name given to it in Lua) is add_key (and not with another name).

If the function should return a value, and that value is printed, it'll not print, if the type of that result value is then inspected (with type) the simulator crashes.

If I instead add any other function, (for example, if I rename it to _add_key instead, it works properly.

A minimal example is included:

Lua:

import "CoreLibs/object"

function playdate.update()
    print("start")
    print(c_api_add_key())
    print(c_api_add_key1())
    print("end")
end

C:

#include <pd_api.h>

PlaydateAPI* _pd = nullptr;

int add_key([[maybe_unused]] lua_State* L)
{
    _pd->lua->pushInt(1230);
    return 1;
}

int __add_key([[maybe_unused]] lua_State* L)
{
    _pd->lua->pushInt(3210);
    return 1;
}

#ifdef _WINDLL
__declspec(dllexport)
#endif
int eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t arg)
{
    _pd = pd;
    const char* err;
    switch(event)
    {
    case kEventInitLua:
    {
        if (! pd->lua->addFunction(add_key, "c_api_add_key", &err))
        {
            pd->system->error("Failed to add function 'add_key', because: %s", err);
	}
        if (! pd->lua->addFunction(__add_key, "c_api_add_key1", &err))
        {
            pd->system->error("Failed to add function '__add_key', because: %s", err);
	}
    }
	break;
    default:
        break;
    }
    return 0;
}

I have a feeling this has something to do with your compiler -- are you on MSVC? This compiles and runs just fine for me, on a Mac using Clang.

I see one thing that is definitely wrong with your code though -- nullptr is a C++ thing, what you do in C is use NULL.

  • I'm on linux, so not using MSVC
  • nullptr was added relatively recently to C
  • the example I posted doesnt crash, but it doesnt write the right values

Upon closer inspection, you're right -- nullptr was added in C23 and I'm an idiot yet again.
Which compiler are you using for this to happen? I'm getting the correct behavior.

Oh, and does it work on device using arm-none-eabi-gcc?

1 Like

The C compiler is just plain GCC:

[remco@deck dsplash]$ /usr/bin/cc --version
cc (GCC) 14.2.1 20240805

(snipped the extra bit with the copyright notice)

Whether it happens on device is a good one, let me check:

It seems to run 'normally' on the device as well.

Unless you only tried on the device (in which case it still could be a simulator thing), it must be something on my side then, unrelated to the SDK.

Which mystifies me, since I've reproduced this as a minimal example, so I'm fairly confident it isn't my code, we just figured out that it isn't Panic-related either (probably) -- so is this basic thing really going wrong in either the Lua interpreter or the C compiler?

I'm also stumped. Does it change if you declare add_key() static?

1 Like

It does change! It starts to work properly when I make it static.

Weird! I guess the loader is resolving add_key to a different function even though yours and the addFunction call are in the same source file. Seems like a compiler bug? Anyway, glad we found a workaround

1 Like

I think I might've found the culprit too: Turns out add_key is a linux functionality as well (see this page), that's included in libc, so I began to suspect that. Sure enough, I got the same behavior when I tried to name a function keyctl -- again, exact naming of course.

While I haven't included the 'right' headers for this to happen if it all was purely C code, I think it's probably something to do with the way Lua hooks into the system. -- Because I don't think that can detect which headers you used during complication, it'll have to go off the compiled C code (and libraries) in some way.

So it then has 2 functions to 'choose' from, and I think the library one is likely to be the first encountered in the compiled code. -- If I where to guess, the static might work because it's probably looks up static functions first, then non-static ones?

(P.S. It wasn't a big problem to begin with, since I could just rename the function, so a workaround wasn't really needed fortunately. Let's hope finding this page helps some unfortunate dev in the future.)