View RSS Feed

Mattias Gustavsson

Plattformsoberoende Entry Point

Rate this Entry
Arbetet med mitt cross-platform lib fortsätter. Det jag tänkte titta på idag är applikationens "entry point", dvs startpunkten, där som exekveringen börjar.

Många spelmotorer och libs gör så att de tar över körningen helt - de implementerar själva applikationens main eller WinMain, och ger sen möjlighet att extenda motorn via olika hooks eller callbacks. Jag har aldrig gillat den modellen - jag vill hellre att jag som utvecklare ska kunna välja när och hur motorn kickas igång. Det kan finnas kod som jag vill köra innan någon del av motorn har körts.

Ett exempel är följande kod, som jag alltid inkluderar först i WinMain funktionen i alla windows program jag skriver:
Kod:
    #ifdef _DEBUG
        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); /* Get current flag */
        flag |= _CRTDBG_LEAK_CHECK_DF; /* Turn on leak-checking bit */
        _CrtSetDbgFlag(flag); /* Set flag to the new value */


/*        _CrtSetBreakAlloc(0); /* Can be manually commented back in to break at a certain allocation */
    #endif
Denna lilla kod-snutt slår på detektering av minnesläckor, vilket innebär att när programmet avslutas, så skriver det ut i output-fönstret om man har några minnesläckor, hur stora de är, och anger ett allokeringsnummer för varje. Om man skickar detta allokeringsnummer till _CrtSetBreakAlloc, och kör programmet igen, så får man en breakpoint på just den allokeringen. Det här är grymt bra att ha för att hålla koll på sin minneshantering. Men det funkar ju bara om man slagit på det innan allokeringen gjorts.

Så det är ett exempel på sånt man kan vilja göra innan motorn körs igång, men det finns säkerligen många andra - och jag vill inte att mitt lib ska komma ivägen för att kunna göra sånt.

Det jag helst skulle vilja göra, är att låta användaren av mitt lib bara anropa enkla funktioner för att komma åt mitt libs funktionalitet. Som t.ex: (funktionsnamn och parametrar är påhittade för exemplet - det kommer inte att se ut exakt så i det riktiga libbet)
Kod:
int main()
    {
    lib_init();
    while( !lib_keydown( KEY_ESC ) ) 
        {
        lib_drawtext( 10, 10, "Hello world" );
        lib_execute();
        }
    lib_term();
    return 0;
    }
I detta exempel har jag som utvecklare full kontroll över körningen. Jag kan strukturera min enry point och main loop på vilket sätt jag vill, och bara anropa libben när jag behöver dess funktionalitet. Den påtvingar mig inte ett visst sätt att jobba. Det är så här jag vill att det ska se ut.

Ett annat typexempel, som jag inte gillar, är följande:
Kod:
int main()
    {
    lib_run( update_func );
    return 0;
    }

bool update_func()
    {
    lib_drawtext( 10, 10, "Hello world" );
    if( !lib_keydown( KEY_ESC ) )
        {
        return true; // ESC was not pressed, so keep running
        }
    else
        {
        return false; // Exit app
        }
    }
Där update_func anropas en gång per frame. Och även om detta funkar, så gillar jag inte när ett lib tar ifrån mig kontrollen på det här sättet. T.ex, med första alternativet kan jag, om jag skulle vilja, ha två helt olika main loops efter varandra - med det andra alternativet måsteupdate_func skrivas för att kunna hantera båda fallen. Det känns helt enkelt klumpigt.

Så vad är problemet, då är det väl bara att köra på första varianten? Jo, det är vad jag gjort förr i tiden - men då har inte iPhone/iPad varit en av mina plattformar.

På iOS, startar man sin app genom att, från main, anropa UIApplicationMain, som skapar upp enview (iOS motsvarighet till fönster), och kör igång hela applikationsmiljön. Genom att ärva från en klass, och skicka med den tillUIApplicationMain, får man callbacks när olika saker händer - t.ex. när det är dags att rendera, etc. Som i fall två alltså, så som jag inte vill ha det. UIApplicationMain returnerar aldrig - den bara kör tills applikationen stängs. Dessutom måste den köra på huvud-tråden, så man kan inte göra nåt lurigt där lib_init kör igång en andra tråd, så att man därigenom kan få det interface man vill ha. iOS tar kontrollen över huvudtråden, och det finns inget man kan göra åt det (och om man mot förmodan skulle komma på ett sätt, så kommer ens app att failas av apple, med största sannolikhet. iOS ska ha kontrollen över huvudtråden).

Vad har vi då för alternativ kvar? Lösningen är tämligen uppenbar - vi kör vår egen applikation i en andra tråd istället. Så då handlar det mer om att se ifall vi kan skriva ett trevligt interface runt detta.

Kod:
int main()
    {
    lib_run( main_func );
    return 0;
    }

void main_func()
    {
    while( !lib_keydown( KEY_ESC ) ) 
        {
        lib_drawtext( 10, 10, "Hello world" );
        lib_execute();
        }
    }
Det är inte riktigt lika snyggt som i idealfallet (där man har full kontroll över main loopen, inklusive vilken tråd den körs på), men på det stora hela så får man samma fördelar, och kan strukturera sin mainloop som man vill.

Submit "Plattformsoberoende Entry Point" to Digg Submit "Plattformsoberoende Entry Point" to del.icio.us Submit "Plattformsoberoende Entry Point" to StumbleUpon Submit "Plattformsoberoende Entry Point" to Google

Taggar: Ingen Lägg till/redigera taggar
Kategorier
Uncategorized

Comments

  1. PeterWelziens avatar
    Jag gillar din artikelserie. Plattformsoberoende misstänker jag kan vara ganska klurigt att få till, men som kan vara väldigt trevligt att ha, så att man bara behöver skriva koden en gång. Det skall bli spännande att se fortsättningen.