Konsep Stack

Mekanisme pertukaran nilai antara C dan Lua melalui stack pada API C.

Daftar isi

Topik ini menjelaskan stack sebagai satu-satunya saluran pertukaran nilai antara C dan Lua pada API C. Pembahasan mencakup sifat LIFO stack, aturan indeks positif dan negatif, fungsi-fungsi stack yang paling sering digunakan, serta contoh program C yang mendorong dan membaca nilai. Setelah topik ini dibaca, konvensi yang berulang pada hampir semua contoh API C sudah dapat diikuti tanpa kebingungan.

Stack sebagai satu-satunya jembatan#

API C Lua tidak memperbolehkan kode C membaca struktur data Lua secara langsung. Setiap nilai yang berpindah antara kode C dan penerjemah melewati sebuah stack, yaitu tumpukan nilai yang berperan LIFO (last in, first out), yang berarti nilai yang didorong terakhir menjadi nilai pertama yang dibaca kembali. Hampir semua fungsi API menerima objek lua_State dan bekerja pada stack milik objek tersebut, baik dengan mendorong (push) nilai baru, membaca nilai yang ada, maupun melepasnya (pop).

Sifat ini menetapkan pola kerja yang seragam pada kedua arah. Untuk mengirim nilai ke Lua, kode C mendorong nilai ke stack lalu memanggil fungsi API yang mengambilnya, misalnya lua_setglobal. Untuk membaca nilai dari Lua, fungsi API menempatkan nilai pada stack, kemudian kode C membaca dan melepasnya dari posisi tersebut. String yang dibaca melalui lua_tostring pun berlaku hanya selama nilai sumbernya masih ada di stack, sehingga string yang masih diperlukan harus disalin ke variabel C.

Dua sisi berbagi stack yang sama, sehingga keadaannya harus dikelola dengan cermat. Dorongan yang tidak dibereskan menumpuk nilai sisa, sedangkan pelepasan yang berlebihan dapat menghapus nilai milik pihak lain. Tinggi stack dapat diperiksa kapan saja melalui lua_gettop, yang mengembalikan jumlah nilai yang berada di dalamnya.

Aturan indeks positif dan negatif#

Setiap nilai pada stack dapat ditunjuk dengan dua jenis indeks. Indeks positif dihitung dari dasar stack, sehingga indeks 1 menunjuk nilai pertama yang masuk, sedangkan indeks negatif dihitung dari puncak, sehingga indeks -1 selalu menunjuk nilai teratas. Kedua penomoran berlaku serentak pada keadaan stack yang sama, dan kebanyakan fungsi API menerima keduanya.

Diagram berikut memperlihatkan keadaan stack setelah empat nilai didorong, yaitu bilangan 3.5, string “halo”, bilangan 17, dan boolean true. Nilai true yang didorong terakhir menempati puncak, sehingga bernomor indeks 4 sekaligus -1, sedangkan 3.5 yang didorong pertama menempati dasar dengan indeks 1 sekaligus -4.

Keadaan stack Lua setelah empat nilai didorongindeks positifindeks negatifpuncak (teratas)true17"halo"3.54321-1-2-3-4arah dorongandasar (terbawah)
Keadaan stack Lua setelah empat nilai didorong

Perbedaan kedua jenis indeks menjadi penting ketika keadaan stack berubah. Indeks -1 selalu menunjuk puncak stack pada saat fungsi dipanggil, sehingga maknanya bergeser setiap kali ada dorongan atau pelepasan baru, sedangkan indeks positif menunjuk posisi yang tetap namun isinya dapat berubah. Konvensi ini menjelaskan pola yang berulang pada dokumentasi API, misalnya pesan error yang berada di puncak stack dan dibaca melalui indeks -1.

Fungsi stack yang sering digunakan#

Fungsi API yang berhubungan dengan stack dapat dikelompokkan menjadi tiga keluarga, yaitu keluarga mendorong, keluarga membaca, dan keluarga pengatur keadaan stack. Keluarga mendorong menambahkan nilai baru pada puncak, sedangkan keluarga membaca mengubah nilai pada posisi tertentu menjadi tipe C tanpa mengubah stack. Keluarga ketiga menyetel tinggi stack dan lazim dipanggil untuk membereskan nilai sisa setelah sebuah operasi selesai. Fungsi yang paling sering dijumpai dirangkum pada tabel berikut.

FungsiPeran
lua_pushnumbermendorong bilangan ke puncak stack
lua_pushstringmendorong string C ke puncak stack
lua_tonumbermembaca nilai pada indeks tertentu sebagai bilangan
lua_tostringmembaca nilai pada indeks tertentu sebagai string C
lua_getglobalmembaca nilai global Lua dan mendorongnya ke puncak stack
lua_setglobalmengambil nilai dari puncak stack dan menyimpannya sebagai global
lua_settopmenyetel tinggi stack ke indeks tertentu
lua_popmelepas sejumlah nilai dari puncak stack

Fungsi lua_getglobal dan lua_setglobal layak mendapat perhatian khusus karena menjadi titik pertemuan paling sederhana antara kedua sisi. lua_getglobal mendorong salinan nilai global yang diminta, sedangkan lua_setglobal melepas nilai teratas dan menyimpannya pada nama global. Keduanya menjadi tulang punggung pola konfigurasi pada aplikasi host, karena berkas konfigurasi menetapkan nilai pada nama global dan host membacanya kembali melalui fungsi yang sama.

Fungsi lua_settop dan lua_pop mengatur tinggi stack dan sering digunakan berpasangan dengan keluarga lainnya. lua_pop(L, n) didefinisikan sebagai makro yang memanggil lua_settop(L, -n - 1), sehingga keduanya berbagi mekanisme yang sama. Pemilihan di antara keduanya merupakan perkara keterbacaan, karena lua_pop menyatakan maksud melepas nilai secara lebih langsung.

Contoh program C yang bekerja dengan stack#

Program berikut mendorong dua nilai, membacanya kembali dengan indeks positif dan negatif, lalu mengecilkan stack. Kompilasinya memerlukan pustaka Lua, misalnya dengan perintah gcc stack-demo.c -llua -o stack-demo pada lingkungan yang memasang Lua beserta pustaka pengembangannya.

c stack-demo.c
// tidak-dijalankan
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"

int main(void) {
    lua_State *L = luaL_newstate();

    /* dorong dua nilai, stack kini berisi dua nilai */
    lua_pushnumber(L, 3.5);       /* indeks 1 */
    lua_pushstring(L, "halo");    /* indeks 2 dan -1 */

    /* baca nilai dari stack tanpa mengubah isinya */
    printf("puncak (-1) : %s\n", lua_tostring(L, -1));
    printf("dasar (1)   : %g\n", lua_tonumber(L, 1));
    printf("tinggi      : %d nilai\n", lua_gettop(L));

    /* kecilkan stack hingga tersisa satu nilai */
    lua_settop(L, 1);
    printf("sisa        : %d nilai\n", lua_gettop(L));

    lua_close(L);
    return 0;
}
bash
# tidak-dijalankan
# kompilasi program host (butuh pustaka pengembangan Lua)
gcc stack-demo.c -llua -o stack-demo
# jalankan program hasil kompilasi
./stack-demo

Apabila program ini dikompilasi dan dijalankan, baris pertama mencetak halo yang dibaca dari indeks -1, baris kedua mencetak 3.5 dari indeks 1, dan baris ketiga melaporkan tinggi stack bernilai 2. Setelah lua_settop menyetel tinggi stack ke 1, laporan berikutnya menunjukkan sisa satu nilai. Contoh ini memperlihatkan bahwa pembacaan melalui lua_tostring dan lua_tonumber tidak mengubah stack, sedangkan penyusutan hanya terjadi ketika lua_settop atau lua_pop dipanggil.

Ringkasan#

  • Stack adalah satu-satunya saluran nilai antara C dan Lua pada API C, berperan LIFO, dan tidak ada akses langsung ke struktur data Lua.
  • Indeks positif dihitung dari dasar dengan indeks 1 sebagai nilai pertama, sedangkan indeks negatif dihitung dari puncak dengan -1 sebagai nilai teratas.
  • Fungsi API terbagi ke dalam keluarga mendorong, membaca, dan mengatur tinggi stack, dengan lua_getglobal dan lua_setglobal sebagai titik pertemuan yang paling sederhana.
  • Kode C bertanggung jawab membereskan stack agar dorongan sisa tidak menumpuk dan nilai pihak lain tidak terhapus.

Referensi#