PrevUpHomeNext

function push

primer::push is a template function used to push a C++ value to a lua Stack.

Examples
Synopsis
template <typename T>
void push(lua_State * L, const T & t);

In the case of primitive values, it is always a direct call to the corresponding lua_pushX function.

In the case of containers, it usually creates a table, and calls push recursively in order to populate the table.

Supported Types

The following core types are supported:

Type

primer::push

bool

Calls lua_pushboolean.

int

Calls lua_pushinteger.

long

long long

unsigned int

Calls lua_pushinteger.

unsigned long

unsigned long long

float

Calls lua_pushnumber.

double

long double

const char *

Calls lua_pushstring.

std::string

[Caution] Caution

When pushing a signed integral type, integer overflow is not permitted to occur, and primer will not perform overflow checks.

If a push operation requires a narrowing conversion of a signed integer type to LUA_INTEGER, a static_assert will fail, to prevent overflow.

[Caution] Caution

When pushing a floating point type, Primer will not check for overflow or loss of precision.

If a push operation requires a narrowing conversion of a floating point type to LUA_NUMBER, a static_assert will fail, to prevent the implicit loss of precision.

[Note] Note

When pushing an unsigned integral type which is larger than LUA_INTEGER, Primer will implicitly narrow it to the unsigned version of LUA_INTEGER, then perform a portable conversion from that type to LUA_INTEGER.

[Note] Note

If you want your program to be portable no matter how lua is compiled, consider:

- Only pushing 32 bit types, e.g., int, unsigned int, float on most platforms.

- Using LUA_INTEGER and the unsigned version of that in your code, and only push these.

If you just want to compile and don't care about overflow, you can do so with syntax like

primer::push(L, static_cast<LUA_INTEGER>(i));

Or, install custom handling for integer types like long and long long, see below.

Or, define PRIMER_NO_STATIC_ASSERTS. (But this disables other safety checks as well.)

The following ancillary types are supported (see primer::read docs for more info).

Type

primer::push

primer::nil_t

Calls lua_pushnil.

primer::truthy

Calls lua_pushboolean.

primer::stringy

Calls lua_pushstring.

Primer includes additional headers to support some C++ standard containers and and boost containers, which are pushed as tables. See the containers section for details.

Customization

primer::push is defined in terms of a type trait, primer::traits::push.

template <typename T>
void
push(lua_State * L, const T & t) {
  ::primer::traits::push<T>::to_stack(L, t);
}

The trait can be specialized to override primer's behavior for certain types, or to add new behavior for custom types.

For example, suppose we have a simple vector type:

struct vec2i {
  int x;
  int y;
};

Primer could be taught to push vec2i objects as a table with entries t[1] and t[2] using the following code:

namespace primer {
namespace traits {

template <>
struct push<vec2i> {
  static void to_stack(lua_State * L, const vec2i & v) {
    lua_newtable(L);
    lua_pushinteger(L, v.x);
    lua_rawseti(L, -2, 1);
    lua_pushinteger(L, v.y);
    lua_rawseti(L, -2, 2);
  }
  static constexpr int stack_space_needed = 2;
};

} // end namespace traits
} // end namespace primer

The following code snippet shows how the pushed object looks to lua

primer::push(L, vec2i{5, 3});
luaL_loadstring(L,
                "v = ...                          \n"
                "assert(type(v) == 'table')       \n"
                "assert(v[1] == 5)                \n"
                "assert(v[2] == 3)                \n"
                "assert(#v == 2)                  \n");

lua_insert(L, -2); // put script beneath argument when using pcall
assert(LUA_OK == lua_pcall(L, 1, 0, 0));
Stack Space

One aspect of lua that you may easily miss on your first pass through the manual is the note about stack overflow.

The lua stack is dynamically sized. When a lua state is created, it is relatively small. It can grow to a quite large size (many thousands of elements).

Some of the more complex lua calls, like lua_pcall, will manage the checking of stack bounds and ensure that they have enough space.

Most of the primitive lua calls, for efficiency, do not do any such bounds checking.

The function lua_checkstack(lua_State *, int) ensures that the stack has a given amount of extra spaces, and enlarges it if needed. It is similar to std::vector::reserve, except that the input refers to additional spaces from the current top of the stack. (lua_checkstack can conceivably fail, if lua runs out of memory.)

lua leaves it up to you to manage this -- if you don't do it, simple programs will work (because of a small built-in minimum stack size) but large programs may give undefined behavior.

In Primer

Most primer::push operations are primitive operations and we don't really want to call lua_checkstack for each one. It's more economical to gather such checks into large groups, so that the checks are performed before a large operation.

So, a custom push specialization has two options -- it can call lua_checkstack itself, or it can assume that the caller will do it. To tell the caller how much space it needs, it must declare the amount using

static constexpr int stack_space_needed = ...

More complex operations in Primer use this information to calculate at compile-time how much stack space they need.

For instance, Primer computes how much space is needed to push std::map<std::string, int> based on it knowledge of how much space is needed for std::string, and int. When a primer::bound_function is called, it computes at compile-time how much stack space it needs to push all of the arguments and calls lua_checkstack to make sure it has enough space.

Querying the Stack Space Needed

The space needed for any individual type can be obtained as a constexpr using

template <typename T>
constexpr int stack_space_for_push();

You can use such functions to get the correct lua_checkstack values into your program without hard-coding them -- invariably such values tend to become inaccurate after a refactor at a later date.

[Note] Note

stack_space_needed should be at least one, since you are pushing something.

If your type is complex enough that no fixed estimate of the stack space is possible, then you should simply define it to be one, and your implementation must call lua_checkstack manually to ensure correct behavior.

push_each

primer::push_each is an extension which pushes a sequence of objects.

primer::push_each(L, a, b, c);

is equivalent to

primer::push(L, a);
primer::push(L, b);
primer::push(L, c);

PrevUpHomeNext