lite-C Structs

Top  Previous  Next

Stellen wir uns mal vor, dass Ihr Chef eines Tages zu Ihnen kommt und sagt: "Jimmy (hier eigenen Namen einsetzen), wir haben einen Auftrag von der Regierung bekommen. Sie hörten von Ihren lite-C Fähigkeiten und möchten, dass SIE Ihre nächste Agentendatenbank Anwendung schreiben." Aufgeregt schauen Sie in den Spiegel und fragen sich: "Ist es das? Ist dies meine erste Chance auf ein Projekt, das mir 100 Euro einbringen wird?"

 

Also machen Sie sich an die Arbeit und schon 30 Minuten später ist Ihr erstes Projekt fertig. Ihr (nicht so schlauer) Chef besteht darauf, dass Sie IHM erklären, wie der Code funktioniert, damit ER ihn der "Bee Eye Agency" vorstellen kann. Sie sind nur Junior Programmierer und müssen sich den Anweisungen fügen, also zeigen Sie ihm den Code (structs1.c) und erklären, wie er funktioniert:

 

STRING* name_str = "#50"; // holds up to 50 characters

STRING* specialties_str = "#20"; // holds up to 20 characters

STRING* status_str = "#10"; // holds up to 10 characters

 

typedef struct {        

       char* name;

       var age;        

       char* specialties;

       var success_rate; // 0...100

       BOOL active; // active / inactive

} AGENTS;

 

Chef, ein Struct ist eine Mischung verschiedener Daten, die unter einem Dach zusammengefasst werden, um dem Programmierer die Arbeit zu erleichtern. Es kann alle möglichen Variablen, Strings, Entities, Pointer und so weiter enthalten. Selbst die lite-C Entities sind Structs, sie enthalten Variablen wie x, y, z, pan, tilt, roll, skill1 bis skill100, flag1, etc. Das obige Struct trägt den Namen AGENTS.

 

AGENTS* dreamer =

{        

       name = "Michael Brent";

       age = 24;

       specialties = "Specops";

       success_rate = 84;

       active = TRUE; // TRUE or FALSE

}

 

Nun haben wir einen der AGENTS erstellt und ihn "dreamer" genannt. Wie Sie sehen ist sein richtiger Name Michael Brent und wir haben alle Einträge mit Daten gefüllt. Es ist allerdings im Allgemeinen nicht notwendig, immer alles mit Daten zu versehen. Oh und beachten Sie die BOOL Variable "active", die entweder den Wert TRUE oder FALSE haben kann und die uns helfen soll festzulegen, ob der Agent im aktiven Dienst ist oder nicht.

 

PANEL* output_pan =

{        

       digits (280, 120, "Agent name: %s ", *, 1, name_str);

       digits (280, 135, "Age: %.0f", *, 1, dreamer.age);

       digits (280, 150, "Specialties: %s ", *, 1, specialties_str);

       digits (280, 165, "Success rate percentage (0...100): %.0f", *, 1, dreamer.success_rate);

       digits (280, 180, "Status: %s ", *, 1, status_str);

       flags = SHOW;

}

 

Das obige Panel zeigt die Informationen auf dem Bildschirm an. Keine Besonderheit daran.

 

function main()

{        

       str_cpy(name_str, dreamer.name);

       str_cpy(specialties_str, dreamer.specialties);

       if (dreamer.active == TRUE)

       {

               str_cpy(status_str, "ACTIVE");                

       }

       else

       {

               str_cpy(status_str, "INACTIVE");                

       }

}

 

Die main Funktion kopiert die Strings unserer Struktur in externe Strings (name_str, specialties_str, status_str), damit wir sie auf dem output_pan Panel anzeigen können. Die "digits" Anweisung in Panels funktioniert nicht mit Zeigern, also können wir etwas wie dreamer.name nicht verwenden. Schließlich setzen wir status_str auf "ACTIVE" oder "INACTIVE", je nach dem Wert von dreamer.active. Das ist alles!

 

Ihr Chef ist ziemlich aufgeregt; er denkt, dass er verstanden hat wie Ihr Programm funktioniert (auch wenn das nicht der Fall ist). Er nimmt Ihre Anwendung, läuft zu den Offiziellen der Bee Eye Agency und diese sind sehr beeindruckt. Einige sind so überrascht, dass sie aus ihren bequemen Sesseln fallen! Schauen Sie selbst - wer wäre nicht beeindruckt?

 

aum83_pnp1

 

Die Offiziellen wollen Sie sofort anwerben. Als erstes möchten sie, dass die Adressen der Agenten in der gleichen Datenbank stehen (sie müssen diese ja schließlich kontaktieren können). Außerdem wollen sie die Daten von bis zu 10.000 Agenten aus der Datei agents.txt auslesen können. Die Bezahlung übertrifft Ihre Erwartungen (120 Euro!), also beginnen Sie sofort mit der Arbeit.

 

Einige Wochen später sind Sie immer noch verwirrt darüber, das an dem folgenden Code falsch ist (structs2.c):

 

STRING* address_str = "#100"; // holds up to 100 characters

 

typedef struct {        

       STRING* address;

} AGENTS;

 

AGENTS taskforces[10000];

 

PANEL* output_pan =

{        

       digits (280, 135, "Address: %s ", *, 1, address_str);

       flags = SHOW;

}

 

function main()

{        

       var file_handle;

       file_handle = file_open_read("agents1.txt");

       file_str_read(file_handle, taskforces[0].address);

       file_close(file_handle);

       str_cpy(address_str, taskforces[0].address);

}

 

Sie haben es geschafft, den Bug mit Hilfe weniger Codezeilen zu erzeugen: Sie definieren ein Struct mit einem einzelnen STRING* und definieren dann "taskforces", wo bis zu 10000 Agenten gespeichert werden können, wie gewünscht. Die Funktion main öffnet die Datei agents1.txt, liest die erste Adresse aus und weist diese taskforces[0].address zu. Die Textdatei wird geschlossen und der String wird in address_str kopiert, um angezeigt zu werden.

 

Und schon kommt die hässliche Fehlermeldung, die Sie schon für Wochen verfolgt:

 

aum83_pnp2

 

Ein "empty Pointer" Fehler in main? Wieso? Falls Sie die rote Zeile auskommentieren, dann verschwindet der Fehler, aber Sie brauchen die Zeile, weil sie die Adresse aus agents1.txt auslesen soll!

 

aum83_pnp3

 

"Diese 120 Euro werden nie mir gehören!" Müde und geschlagen suchen Sie ein Stück Seil, entschlossen Ihrem Elend ein Ende zu setzen. Sie fühlen Sich so... NULL!

Und plötzlich haben Sie eine Idee. Könnte es sein, dass der "address" String in der Struktur NULL ist, also keine Daten aus der Textdatei aufnehmen kann? Sie eilen zurück zu Ihrem Computer und tippen wir wild drauf los...

 

STRING* address_str = "#100"; // holds up to 100 characters

 

typedef struct {        

       STRING* address;

} AGENTS;

 

AGENTS taskforces[10000];

 

PANEL* output_pan =

{        

       digits (280, 135, "Address: %s ", *, 1, address_str);

       flags = SHOW;

}

 

function main()

{        

       var file_handle;

       file_handle = file_open_read("agents1.txt");

       taskforces[0].address = str_create("");

       file_str_read(file_handle, taskforces[0].address);

       file_close(file_handle);

       str_cpy(address_str, taskforces[0].address);

}

 

JA! Die neue, grüne Codezeile erzeugt zur Laufzeit einen String und kopiert seinen leeren Inhalt in "address" und ersetzt damit den NULL String (der aus nichts besteht!) durch einen leeren String, was etwas gänzlich anderes ist. Die Felder des Structes nach der Erzeugung zu initialisieren ist eine Lektion, sie Sie noch lange behalten werden...

 

Alles funktioniert nun und Sie können die Adressen ohne Probleme lesen.

 

aum83_pnp4

 

Nun können Sie endliche weiterarbeiten! Die Deadline ist schon sehr nahe, aber Sie arbeiten zwei volle Tage ohne Schlaf und Essen und stellen diese wundervolle Anwendung (struct3.c) fertig, die genau das Gewünschte leistet.

 

aum83_pnp5

 

Ihr Chef ist begeistert! Er kann mit den Pfeiltasten durch die Agentenliste scrollen. Die Daten werden aus agents2.txt eingelesen, einer normalen Textdatei, die einfach editiert werden kann. Er möchte mehr über dieses Projekt erfahren.

 

var taskforce_age;

var taskforce_success;

var i = 0;

 

STRING* name_str = "#50"; // holds up to 50 characters

STRING* specialties_str = "#20"; // holds up to 20 characters

STRING* status_str = "#10"; // holds up to 10 characters

STRING* address_str = "#100"; // holds up to 100 characters

STRING* temp_str = "#20"; // just a temporary string

 

typedef struct {        

       STRING* name;

       var age;        

       STRING* specialties;

       var success_rate; // 0...100

       BOOL active; // active / inactive

       STRING* address;

} AGENTS;

 

AGENTS taskforces[10000];

 

PANEL* output_pan =

{        

       digits (220, 120, "Agent name: %s ", arial_font, 1, name_str);

       digits (220, 145, "Age: %.0f", arial_font, 1, taskforce_age);

       digits (220, 170, "Specialties: %s ", arial_font, 1, specialties_str);

       digits (220, 195, "Success rate percentage (0...100): %.0f", arial_font, 1, taskforce_success);

       digits (220, 220, "Status: %s ", arial_font, 1, status_str);

       digits (220, 245, "Address: %s ", arial_font, 1, address_str);

       flags = SHOW;

}

 

Der interessante Teil geschieht in der main Funktion. Wir öffnen agents2.txt und lesen alle Daten aus bis wir das Ende der Datie erreichen (bis file_str_read -1 zurückliefert). Die while Schleife kann bis 10000 laufen, wird aber vermutlich vorher aufhören, es sei denn, es gibt mehr als 10000 Einträge in agents2.txt.

 

function main()

{        

       var file_handle;

       file_handle = file_open_read("agents2.txt");

       while (i < 10000)

       {

               taskforces[i].name = str_create("");

               if (file_str_read(file_handle, taskforces[i].name) == -1) // the end of file was reached?

               {

                       break; // then get out of this loop!

               }

               else

               {

                       file_str_read(file_handle, temp_str);

                       taskforces[i].age = str_to_num(temp_str);

                       taskforces[i].specialties = str_create("");

                       file_str_read(file_handle, taskforces[i].specialties);

                       file_str_read(file_handle, temp_str);

                       taskforces[i].success_rate = str_to_num(temp_str);

                       file_str_read(file_handle, temp_str);

                       if (str_cmpi(temp_str, "TRUE"))

                       {

                               taskforces[i].active = TRUE;

                       }

                       else

                       {

                               taskforces[i].active = FALSE;

                       }

                       taskforces[i].address = str_create("");

                       file_str_read(file_handle, taskforces[i].address);

                       i++;

               }

       }

       file_close(file_handle);

}

 

All die Daten aus agents2.txt (mitsamt der Zahlenwerte) werden mit file_str_read ausgelesen und dann entsprechend umgewandelt. Es ist immer eine gute Idee (und sicherer) dies zu tun. Die BOOL Variable wird als String gelesen und falls sie "TRUE" ist, wird taskforces[i].active auf TRUE gesetzt, andernfalls auf FALSE. Sobald die while Schleife beendet ist, haben wir alle nötigen Daten in der taskforces Struktur gespeichert und agents2.txt wird geschlossen.

 

function info_startup()

{

       wait (-1); // wait until all the data is loaded

       var counter;

       while (1)

       {

               if (key_cud)

               {

                       while (key_cud) {wait (1);}

                       counter++;

               }

               if (key_cuu)

               {

                       while (key_cuu) {wait (1);}

                       counter--;

               }

               counter = clamp(counter, 0, i-1);

               str_cpy(name_str, taskforces[counter].name);

               taskforce_age = taskforces[counter].age;

               str_cpy(specialties_str, taskforces[counter].specialties);

               taskforce_success = taskforces[counter].success_rate;

               if (taskforces[counter].active == TRUE)

               {

                       str_cpy(status_str, "ACTIVE");

               }

               else

               {

                       str_cpy(status_str, "INACTIVE");

               }        

               str_cpy(address_str, taskforces[counter].address);

               wait (1);

       }

}

 

Die Funktion info_startup() zeigt die Daten an. Wir warten eine Sekunde und stellen auf diese Weise sicher, dass die Daten verfügbar sind, selbst wenn der Computer eine antike Festplatte hat und dann vergrößern oder verringern wir eine Variable namens "counter" je nachdem, welche Pfeiltaste gedrückt ist. Die "clamp" Anweisung begrenzt die counter Variable auf die sinnvollen Werte (diejenigen "taskforces" Einträge, die Daten enthalten) und dann kopieren wir diese Daten in die Variablen und Strings, die vom Panel angezeigt werden.

 

Ihr Chef ist wieder mal zufrieden. Er hat zwar kein Wort verstanden, aber er nickt und lächelt und tut so als wüsste er, wovon Sie reden. Einige Tage vergehen, in denen Sie von den 120 Euro träumen (die in vier Jahresraten gezahlt werden).

 

Gute Neuigkeiten! Ihr Chef hat Ihnen mitgeteilt, dass die Leute von Bee Eyes ihn erneut kontaktiert haben. Sie haben diese Detektivserie im Fernsehen gesehen und sahen dort einen Bildschirm, der Agenten wie Spider oder Strider als rotierende 3D Charaktere anzeigte. Kann so etwas gemacht werden? Und falls die Antwort positiv ausfällt, hätten sie gerne einen 3D Kalender mit ihren Top 100 Geheimagenten und diesen als Freeware zum Download freigeben, um ihre Firma zu promoten. Die Bezahlung ist einmalig (125 Euro), also beginnen Sie gleich mit der Arbeit.

 

FONT* arial_font = "Arial#20";

 

var i = 0; // used as an index

var active_agents = 0; // removes the old agent models that aren't used anymore

 

STRING* name_str = "#30"; // used to display the name of the agent, holds up to 30 characters

 

TEXT* models_txt =

{

       strings = 100; // store up to 100 model names

}

 

PANEL* output_pan =

{        

       digits (120, 120, "Agent name: %s ", arial_font, 1, name_str);

       flags = SHOW;

}

 

Sie werden sich diesmal selbst übertreffen und einen großartigen Kalender erstellen. Die Anwender müssen lediglich ein neues 3D Model in den Projektordner kopieren und es wird automatisch geladen. Desweiteren wird der Name des Models direkt aus dem Dateinamen übernommen. Zunächst erstellen Sie einen Text mit 100 Strings, in den die Namen der Agenten kommen.

 

typedef struct {

       STRING* name;

       ENTITY* agent_model;

} AGENTS;

 

AGENTS* taskforces[100];

 

Der obige Code ist eine typische Struct Definition. Er enthält einen String Pointer (der Name des Agenten), einen Entity Pointer (das verwendete Model). Danach definieren wir einen Pointer auf die AGENTS Struktur namens taskforces. Dort werden die Daten der 100 Agenten gespeichert.

 

function main()

{

       var counter = 0;

       level_load (NULL);

       int j;

       for(j = 0; j < 100; j++)

               taskforces[j] = malloc(sizeof(AGENTS));

       txt_for_dir(models_txt, "*.mdl"); // loads the model names

       while (str_len((models_txt.pstring)[i]) > 0)

       {

               i++;

       }

       i--;

       while (1)

       {

               active_agents = 0; // remove the previous agent model

               wait (2); // give it enough time to be removed

               active_agents = 1; // keep the new agent alive

               taskforces[i] = create_agent((models_txt.pstring)[counter]);

               str_cpy(name_str, taskforces[i].name);

               while (!key_cud && !key_cuu) {wait (1);}

               counter += key_cud - key_cuu;

               counter = clamp(counter, 0, i);

               while (key_cud || key_cuu) {wait (1);}

       }

}

 

Die Funktion main beginnt mit einem leeren Level, da wir eine 3D Umgebung brauchen um 3D Models anzeigen zu können. Die folgenden 3 Codezeilen sind dafür da, Speicherplatz für unsere "taskforces" Agenten bereitzustellen. Dies müssen wir immer tun, wenn wir mit großen Datenstrukturen arbeiten, da wir sonst Gefahr laufen, dass die Engine abstürzt, die nur einen begrenzten Platz für lokale Daten einräumt. Diese Demo würde auch ohne diese Maßnahme laufen, aber sicher ist sicher.

 

Die folgende Codezeile speichert alle Model Namen im String models_txt. Die kleine Schleife läuft bis sie auf einen String der Länge 0 stößt; dann ist das Ende der Liste erreicht. Wir verringern den Wert von "i", weil dieser kurz zuvor in der Schleife nochmal erhöht wurde, damit der Wert von i genau die Zahl der verfügbaren Models angibt.

 

Die folgende Schleife setzt active_agents für 2 Frames auf 0. Falls ein Model sichtbar ist, wird es entfernt. Falls active_agents wieder auf 1 gesetzt wird, sorgt dies dafür, dass ein neues Agentenmodel beibehalten wird.

 

Die folgende Zeile in der Schleife sieht etwas komplizierter aus:

 

               taskforces[i] = create_agent((models_txt.pstring)[counter]);

 

Aber dennoch ist sie ganz einfach: die create_agent Funktion wird aufgerufen, die dann die Felder von taskforces[i] ausfüllt. Diese Funktion erhält einen Parameter - den Namen des Models aus dem models_txt String. Daher würde ein typischer Aufruf so aussehen:

 

               taskforces[2] = create_agent("Mummy.mdl"); // if Mummy.mdl would be the 2nd model read from the project's folder

 

Die restlichen Zeilen der Schleife kopieren den Namen des Models auf den Bildschirm, warten bis wir key_cuu oder key_cud drücken und verändern damit die counter Variable, legen den sinnvollen Bereich für diese Variable fest und warten, bis die Pfeiltasten wieder losgelassen wurden.

 

function create_agent(STRING *agent_name)

{

       AGENTS* temp_agent = malloc(sizeof(AGENTS));

       temp_agent.name = str_create(agent_name);

       temp_agent.agent_model = ent_create(agent_name, vector(130, 0, -10), rotate_agent); // create the agent model

       str_trunc(temp_agent.name, 4); // now cut its name tail (.mdl) because we don't want to display it

       return temp_agent; // returns the address of the struct for further use (might be needed in the future)

}

 

Die Funktion create_agent füllt die Felder der Struktur. Wir definieren einen Agenten namens "temp_agent" und reservieren erneut Speicherplatz dafür. Der Name wird mit str_create auf den übergebenen Parameter gesetzt (dies haben wir auf die harte Tour beim letzten Projekt gelernt) und dann erstellen wir das 3D Model mit dem gegebenen Namen bei X = 130, Y = 0 und Z = -10 mit der Funktion rotate_agent. Schließlich löschen wir die letzten 4 Zeichen des Namens, damit der String als "Hero" und nicht "Hero.mdl" angezeigt wird. Die letzte Codezeile liefert die Adresse der Struktur für spätere Verwendung zurück. Wir brauchen diese hier nicht, aber sie könnte sich später als nützlich erweisen.

 

function rotate_agent()

{

       while (active_agents)

       {

               my.pan += 4 * time_step;

               wait (1);        

       }        

       ent_remove(my); // remove the agent model when it isn't needed anymore

}

 

Diese letzte kleine Funktion dreht das Model durch Änderung des pan Winkels solange active_agents nicht 0 ist. Wie Sie wissen wird dieser Wert für 2 Frames auf 0 gesetzt wenn ein neues Model erstellt wird, damit das alte entfernt wird.

 

aum83_pnp6

 

Die Leute von Bee Eyes sind begeistert! Die Anwendung leistet exakt was sie wollten und mehr! Sie werden definitiv mit Ihnen im Geschäft bleiben.

Als Sie mit dem Artikel fertig sind, ist das Geld auch da. Ihr Chef nimmt sich 72% wie vereinbart, aber raten Sie mal, wer die lite-C Anwendung schreiben soll, die das ausrechnet...