C Lua: Functions

Kaynaklar

İkili modüller oluşturmak için IDE'nizi henüz kurmadıysanız, aşağıdaki sayfalardan birine bakın:

Daha önce hiç C++ kullanmadıysanız, temel bilgileri edinmenize yardımcı olacak bir eğitim mevcuttur.

Lua C API Dokümantasyonu, Lua'nın C API'sini anlamak için son derece faydalı bir kaynaktır.

Ek faydalı fonksiyonlar için LuaBase.h dosyasına başvurabilirsiniz.

Bu, daha kolay gezinmenizi sağlamak için bu sayfadaki tüm bölümleri içeren bir listedir.

Yığınları (stack) anlama

Lua'nın C API'si yığınlar (stack) üzerine kuruludur ve bunları anlamak kritik öneme sahiptir.

Yığın, kendisine elemanların "pushed" (itilebildiği) ve ondan "popped" (çekilebildiği) bir tür dizidir.

Bir elemanı itmek (pushing) onu yığının en üstüne koyarken, yığından çekmek (popping) en üstteki elemanı kaldırır.

Yığınların kısıtlayıcı doğasını atlayarak, yığındaki belirli konumlara değer eklemek ve kaldırmak için Remove ve Insert fonksiyonlarını kullanmak da mümkündür, ancak bunları idareli kullanmalısınız.

Yığın konumlarını alırken sık sık negatif sayılar göreceksiniz. 1 yığının en altını veya ilk elemanı ifade ederken, -1 yığının en üstünü veya son/en yeni elemanı ifade eder. -1 genellikle yığının en üstüne yeni itilmiş değeri almak için kullanılır. -2 en üstten ikinci veya sondan ikinci değeri alır.

Lua'nın C API'sindeki belirli fonksiyonların yığınla potansiyel olarak beklenmedik şekillerde etkileşime girdiğini ve hatalarla karşılaşıp yığının takibini kaybederseniz, kullandığınız fonksiyonları araştırmanız gerektiğini unutmamak önemlidir.

Güçlü bir örnek Call fonksiyonudur. C API'sinden bir fonksiyonu çağırmak, fonksiyonu ve her argümanı yığından çeker (pop).

Aşağıdaki kod parçacığı math.floor(5.6) fonksiyonunu çağırır ve geri dönüş değerini bir double MyDouble içinde saklarken, mevcut yığının bir görselleştirmesini tanıdık bir dizi formatında yorum olarak gösterir:

double MyDouble; // Global tabloyu al, [Global{}] LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB); // math tablosunu al, [Global{}, math{}] LUA->GetField(-1, "math"); // floor fonksiyonunu al, [Global{}, math{}, floor()] LUA->GetField(-1, "floor"); // double 5.6'yı it, [Global{}, math{}, floor(), 5.6] LUA->PushNumber(5.6); // 1 argümanlı ve 1 geri dönüş değerli bir fonksiyon çağır // Bu, floor() fonksiyonunu ve 5.6 argümanını yığından çeker (pop), ardından geri dönüş değerini yığının en üstüne iter (push) // Yığın şimdi [Global{}, math{}, 5] LUA->Call(1, 1); // Yığının en üstündeki sayıyı alır, ÇEKMEZ (POP ETMEZ), ve MyDouble içinde saklar, [Global{}, math{}, 5] MyDouble = LUA->GetNumber(-1); // Global{}, math{} ve 5'i yığından çeker (pop), [] LUA->Pop(3);

Lua için C++ fonksiyonları oluşturma

Lua için C++ fonksiyonları yapmak, ikili dosyalarınız (binary) için kesinlikle gereklidir. Bu olmadan, modülleriniz o kadar da kullanışlı olmaz. Bu bölümde, Lua'da kullanılmak üzere C++'da fonksiyonlar oluşturmayı, argümanları kontrol etmeyi ve değerleri döndürmeyi ele alacağım.

Fonksiyonumuzu C++'da Tanımlama

Yapacağımız ilk şey C++ fonksiyonumuzu tanımlamaktır.

LUA_FUNCTION( MyFirstFunction ) { //Buraya birazdan bir şeyler ekleyeceğiz }

Şimdi boş bir fonksiyonumuz olduğuna göre, bir değer döndürmesini sağlayalım. Bunu yapmak için yığına bir şey iteriz ve ardından döndürmek istediğimiz değer sayısını geri döndürürüz. Yani bu durumda bir bool, true, iteceğiz ve sonra 1 döndüreceğiz.

LUA_FUNCTION( MyFirstFunction ) { LUA->PushBool( true ); // bool değerimizi yığına it. return 1; // Kaç değer döndürdüğümüz }

Şimdi bu fonksiyonu Lua'da çağırsaydık, ki henüz yapamayız çünkü sadece C++'da tanımladık, o zaman her zaman true döndürürdü.

Pekala, şimdi nasıl çalıştığına dair temel bir anlayışa sahip olduğunuza göre, onu daha karmaşık hale getirelim. Bir parametre ekleyelim ve belirli bir değerin üzerindeyse true, aksi takdirde false döndürelim.

LUA_FUNCTION( MyFirstFunction ) { LUA->CheckType( 1, GarrysMod::Lua::Type::Number ); // İlk argümanın bir sayı olduğundan emin ol double number = (LUA->GetNumber( 1 )); // İlk argümanı al if (number > 9.0) // Eğer sayı 9'dan büyükse... { LUA->PushBool( true ); // true it... } else { LUA->PushBool( false ); // değilse, false it. } return 1; // Kaç değer döndürdüğümüz }

C++ Fonksiyonumuzu Lua'da Tanımlama

Şimdi fonksiyonumuzu oluşturduğumuza göre, onu Lua'da çağırmanın bir yoluna ihtiyacımız var. Bu kolay kısım.

GMOD_MODULE_OPEN() { LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->PushCFunction( MyFirstFunction ); // Fonksiyonumuzu it LUA->SetField( -2, "MyFirstFunction" ); // Lua'daki MyFirstFunction'ı bizim C++ fonksiyonumuza ayarla LUA->Pop(); // Global tabloyu yığından çek return 0; }

Bu, Lua'daki "MyFirstFunction" değişkenini C++'daki "MyFirstFunction" fonksiyonuna ayarlar, böylece Lua'da şu şekilde çağırabilirsiniz:

MyFirstFunction( <number> )

C++ İçinde Lua Fonksiyonu Çağırma

Lua C kullanarak Lua fonksiyonlarını nasıl çağıracağınızı bilmek çok faydalıdır. Bu bölümde, fonksiyonları çağırmayı ve geri dönüş değerlerini almayı ele alacağım. Şimdi Call'un nasıl çalıştığına dair kısa bir açıklama. Aşağıdakini yapsaydım:

LUA->Call( 0, 0 );

O zaman fonksiyonumu 0 argümanla çağırır ve bir geri dönüş değeri almazdı. Bir sonraki örnek 1 argümanla çağırır ve yine geri dönüş değeri almazdı:

LUA->Call( 1, 0 );

Bir sonraki örneğim 3 argüman geçirir ve 1 geri dönüş değeri alırdı:

LUA->Call( 3, 1 );

Son olarak son örneğim 2 argüman geçirir ve 4 geri dönüş değeri alırdı:

LUA->Call( 2, 4 );

Call ve PCall

Call ve PCall arasındaki tek fark, PCall'un korumalı çağrı (protected call) anlamına gelmesidir. Başka bir deyişle, bir şeyler ters giderse, PCall panik yapmaz ama Call yapar. Dolayısıyla argümanlarınız sabit değilse PCall kullanmak iyi bir alışkanlıktır, ancak bu örneklerde Call kullanacağız.

Global Tablodan Çağırma

Yani global tablodan çağırma muhtemelen en çok kullanacağınız şeydir. Global tablodan çağırmak çok basit ve kolaydır. Sadece çok güzel görünmüyor.

LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->GetField( -1, "print" ); // print fonksiyonunu al LUA->PushString( "Swag" ); // Argümanımızı it LUA->Call( 1, 0 ); // Fonksiyonu çağır LUA->Pop(); // Global tabloyu yığından çek

Bu, konsola "Swag" çıktısı verirdi. Aşağıda birden fazla argüman kullanma örneği bulunmaktadır.

LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->GetField( -1, "print" ); // print fonksiyonunu al LUA->PushString( "Swag" ); // Argümanımızı it LUA->PushNumber( 1337 ); // İkinci argümanımızı it LUA->Call( 2, 0 ); // Fonksiyonu çağır LUA->Pop(); // Global tabloyu yığından çek

Bu, konsola "Swag 1337" çıktısı verirdi. Aşağıda sözde-pratik bir kullanım örneği bulunmaktadır.

LUA_FUNCTION( MyFirstFunction ) { LUA->CheckType( 1, GarrysMod::Lua::Type::String ); // İlk argümanın bir string olduğundan emin ol LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->GetField( -1, "print" ); // print fonksiyonunu al LUA->Push( 1 ); // İlk argümanı it LUA->Call( 1, 0 ); // Fonksiyonu çağır LUA->Pop(); // Global tabloyu yığından çek return 0; // Kaç değer döndürdüğümüz }

Birden fazla fonksiyon çağırmak isteseydiniz, aşağıdakini yapardınız:

LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->GetField( -1, "print" ); // print fonksiyonunu al LUA->PushString( "Swag" ); // Argümanımızı it LUA->Call( 1, 0 ); // Fonksiyonu çağır LUA->GetField( -1, "print" ); // print fonksiyonunu al LUA->PushString( "Garry feels like a new man." ); // Argümanımızı it LUA->Call( 1, 0 ); // Fonksiyonu çağır LUA->GetField( -1, "Msg" ); // Msg fonksiyonunu al LUA->PushString( "Double swag\n" ); // Argümanımızı it LUA->Call( 1, 0 ); // Fonksiyonu çağır LUA->Pop(); // Global tabloyu yığından çek

Argüman Olarak Geçirilen Fonksiyonları Çağırma

Argüman olarak geçirilen fonksiyonları çağırmak çok faydalıdır. Başka bir bölümde bir fonksiyonu nasıl saklayacağınızı ve daha sonra nasıl çağıracağınızı ele alacağım, ama şimdilik temel bilgilerle başlayacağız.

LUA_FUNCTION( MyFirstFunction ) { LUA->CheckType( 1, GarrysMod::Lua::Type::Function ); // İlk argümanın bir fonksiyon olduğundan emin ol LUA->PushString( "Hey... swag swag swag swag" ); // Argümanımızı it LUA->Push( 1 ); // Fonksiyonu it LUA->Call( 1, 0 ); // Fonksiyonu çağır return 0; }

Şimdi Lua'da aşağıdaki gibi bir şey yapsaydınız, konsola "Hey... swag swag swag swag" çıktısı verirdi.

MyFirstFunction( print )

Geri Dönüş Değerini Alma

Yani fonksiyonları nasıl çağıracağınızı biliyorsunuz, sadece geri dönüş değerini nasıl alacağınızı bilmiyorsunuz. Bunu sizin için düzelteceğim. Aşağıda math.abs'in geri dönüş değerini alma örneği bulunmaktadır:

LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->GetField( -1, "math" ); // math tablosunu al LUA->GetField( -1, "abs" ); // math tablosundan abs fonksiyonunu al LUA->PushNumber( -666 ); // Argümanımızı it LUA->Call( 1, 1 ); // Fonksiyonu çağır ve 1 geri dönüş değeri al int iReturnValue = (int)LUA->GetNumber( -1 ); LUA->Pop( 3 ); // Global tabloyu, math tablosunu ve geri dönüş değerini yığından çek

Şimdi iReturnValue içinde math.abs( -666 ) fonksiyonunun geri dönüş değerine sahibiz. Yani iReturnValue 666'ya eşittir. Aşağıdaki örnekte, math.abs( 1337 ) sonucunu alıp konsoluma yazdırıyorum.

LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Global tabloyu it LUA->GetField( -1, "math" ); // math tablosunu al LUA->GetField( -1, "abs" ); // math tablosundan abs fonksiyonunu al LUA->PushNumber( -666 ); // Argümanımızı it LUA->Call( 1, 1 ); // Fonksiyonu çağır ve 1 geri dönüş değeri al LUA->GetField( -3, "print" ); // print fonksiyonunu al LUA->Push( -2 ); // Geri dönüş değerini it LUA->Call( 1, 0 ); // print fonksiyonunu çağır LUA->Pop( 3 ); // Global tabloyu, math tablosunu ve geri dönüş değerini yığından çek

Bu, konsoluma "666" çıktısı verirdi.

Bir Lua Tablosu Üzerinde Yineleme.

Bir noktada, LUA'dan geçirilen bir tablo üzerinde yineleme yapmak isteyeceksiniz. Bunu yapabileceğiniz yollardan biri aşağıda gösterilmiştir.

Bir anahtar veya değer tablo gibi bir şeyse muhtemelen bir tür kontrolü eklemelisiniz, çünkü değer bir tablo iken (diğer türler için de olabilir) LUA->GetString çağırmak bir bellek sızıntısına neden olur!
LUA_FUNCTION(Example) { LUA->CheckType(1, Type::Table); LUA->PushNil(); while (LUA->Next(-2)) { LUA->Push(-2); const char* key = LUA->GetString(-1); const char* value = LUA->GetString(-2); Msg("%s => %s\n", key, value); // Bunu kendi kodunuzla değiştirin. LUA->Pop(2); } LUA->Pop(); return 0; }

Okunabilirlik ve kolaylık için C++ fonksiyonları oluşturma

Dahili olarak, LUA_FUNCTION() LUA değişkenini ortaya çıkaran ve içerdiği fonksiyonu Lua ile kullanılmak üzere hazırlayan bir makrodur.

Yalnızca C ortamında var olan fonksiyonlar oluşturmak için bu LUA değişkenini kendi fonksiyonlarımıza geçirebiliriz.

Bir fonksiyon aşağıdaki gibi tanımlanabilir:

// print() kullanarak bir mesaj yazdırır void LuaPrint(GarrysMod::Lua::ILuaBase* LUA, char* msg) { LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB); // Global tabloyu al LUA->GetField(-1, "print"); // print fonksiyonunu al LUA->PushString(msg); // String argümanını it LUA->Call(1, 0); // Fonksiyonu 1 argüman ve geri dönüş değeri olmadan çağır LUA->Pop(); // Global tabloyu çek }

...ve sonra bir LUA_FUNCTION veya hatta GMOD_MODULE_OPEN içinden çağrılabilir:

GMOD_MODULE_OPEN() { // Modülümüzü hazırla, Lua fonksiyonlarını yükle, vb... LuaPrint(LUA, "Module successfully loaded!"); // print("Modül başarıyla yüklendi!") }

Verimlilik için, fonksiyonlarda ne zaman inline kullanılacağını anlamak önemlidir.

Userdata ve metatablolar hakkında bir not

Userdata ve metatablolar Garry's Mod'da farklı şekilde ele alınır. Bu, motorun userdata türünü çok daha hızlı belirlemesine yardımcı olur.

Önce metatablonuzu oluşturun (ideal olarak GMOD_MODULE_OPEN içinde), ardından ona bir referans oluşturun ve bunu global olarak bir değişkende saklayın.

LUA->CreateTable(); LUA->PushCFunction(gcDeleteWrapper); LUA->SetField(-2, "__gc"); LUA->PushCFunction(toStringWrapper); LUA->SetField(-2, "__tostring"); LUA->PushCFunction(indexWrapper); LUA->SetField(-2, "__index"); LUA->PushCFunction(newIndexWrapper); LUA->SetField(-2, "__newindex"); metatable = LUA->ReferenceCreate();

Userdata'nızı yığına itmek için:

GarrysMod::Lua::UserData* ud = ( GarrysMod::Lua::UserData* )LUA->NewUserdata( sizeof( GarrysMod::Lua::UserData ) ); ud->data = pointer_to_your_c_class; ud->type = your_type_id; LUA->ReferencePush( metatable ); LUA->SetMetaTable(-2);

Userdata'nızı yığından almak için:

GarrysMod::Lua::UserData* obj = (GarrysMod::Lua::UserData* )LUA->GetUserdata(position); your_c_class* var = (your_c_class*)(obj->data);

Tüm sunucular · GmodTR Launcher · Rehber · GMod Wiki · GmodTR nedir? · Sunucu ekle · Discord