3

Using the developement libraries for Lua 5.3.5 and gcc 9.2.0 I have encountered a weird compilation issue for the following minimal snippet:

#include <functional>

extern "C" {
  #include "lua.h"
  #include "lualib.h"
}

int main()
{
  using namespace std::placeholders;

  auto lua_simple_call = std::bind(lua_call, _1, 0, 0);
}

gcc complains: error: ‘lua_call’ was not declared in this scope. This issue does not occur when trying to simply call lua_call without the use of std::bind and it also does not seem to occur for other Lua C functions like lua_newtable etc. I would like to know what's causing this and how to circumvent it.

Peter
  • 2,919
  • 1
  • 16
  • 35
  • Isn't `lua_call` a macro or something? What is its definition? – YSC Mar 11 '20 at 12:24
  • It is, but I don't see the issue because it simply expands to a call to `lua_callk`. – Peter Mar 11 '20 at 12:27
  • In order to understand why it is not possible, you need to read a bit more about the way preprocessing macros work. Start here: https://stackoverflow.com/questions/1137575/inline-functions-vs-preprocessor-macros and go deeper. Eventually, you'll find a solution. – YSC Mar 11 '20 at 12:33
  • 2
    You can wrap it into lambda: `[](lua_State* s){ lua_call(s,0,0); }` or pass pointer to real function of Lua, not macro: `std::bind(lua_callk,_1,0,0,0,0)`. – rafix07 Mar 11 '20 at 12:41

1 Answers1

7

As OP mentioned, lua_call is a macro which expands to lua_callk but this is only half of the truth.

lua_call is a function macro:

github: lua.h:

#define lua_call(L,n,r)     lua_callk(L, (n), (r), 0, NULL)

and that makes the difference.

So, lua_call will expand to lua_callk only if used with the right number of arguments.

I made an MCVE to demonstrate this:

#include <iostream>

#define lua_call(L, n, r) lua_callk(L, (n), (r))

void lua_callk(void *L, int n, int r)
{
  std::cout << "lua_callk(" << L << ", " << n << ", " << r << ")\n";
}

#define TEST(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  TEST(lua_call(nullptr, 2, 1));
  //TEST(std::cout << "&lua_call: " << &lua_call << '\n');
}

Output:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
lua_call(nullptr, 2, 1);
lua_callk(0, 2, 1)

Live Demo on coliru

versus:

#include <iostream>

#define lua_call(L, n, r) lua_callk(L, (n), (r))

void lua_callk(void *L, int n, int r)
{
  std::cout << "lua_callk(" << L << ", " << n << ", " << r << ")\n";
}

#define TEST(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  TEST(lua_call(nullptr, 2, 1));
  std::cout << "&lua_call: " << &lua_call << '\n');
}

Output:

main.cpp: In function 'int main()':
main.cpp:15:34: error: 'lua_call' was not declared in this scope
   15 |   std::cout << "&lua_call: " << &lua_call << '\n';
      |                                  ^~~~~~~~

Live Demo on coliru

or, to make this even more obvious:

//#include <iostream>

#define lua_call(L, n, r) lua_callk(L, (n), (r))

void lua_callk(void *L, int n, int r)
{
  std::cout << "lua_callk(" << L << ", " << n << ", " << r << ")\n";
}

#define TEST(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ 

int main()
{
  TEST(lua_call(nullptr, 2, 1));
  std::cout << "&lua_call: " << &lua_call << '\n';
}

with pre-processor-only run:

# 1 "main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cpp"


void lua_callk(void *L, int n, int r)
{
  std::cout << "lua_callk(" << L << ", " << n << ", " << r << ")\n";
}


int main()
{
  std::cout << "lua_call(nullptr, 2, 1)" << ";\n"; lua_callk(nullptr, (2), (1));
  std::cout << "&lua_call: " << &lua_call << '\n';
}

Live Demo on coliru


The fix is obvious as well (as already mentioned in Rafix' comment):

Just wrap lua_bind() into something addressable: a function or lambda.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • I feel very stupid now for not realizing this myself, but this is a great explanation anyways! – Peter Mar 11 '20 at 19:25