Pointer to custom struct type

Started by monkey0506, Tue 12/04/2005 03:04:45

Previous topic - Next topic

monkey0506

Okay, I thought I had thoroughly searched the manual, but then in the "Pointers for people who know Java or C#" section I found this:

QuoteBecause AGS does not have a new keyword, you cannot create pointers to custom struct types.

The problem is, I either need a pointer to a custom struct type, or I have to make duplicate copies of the same data.  Any help on this matter would be greatly appreciated.  Since it hasn't been implemented, is it coming?

I've done my homework, searched the forums, the manuals, the tracker...I'm just asking...

I decided to add this to the end of the AGS 2.7 thread.  After searching several of the pages for the word "new"...

Actually, after trying to duplicate the data, I don't think it will be possible.  I'm pretty sure that I need a pointer to my custom struct type.  It won't work the way I want it to otherwise.

strazer

QuoteThe problem is, I either need a pointer to a custom struct type, or I have to make duplicate copies of the same data.

Can you describe in more detail what you need that pointer for?

monkey0506

Basically I want to be able to access the data members of a custom struct via an array of pointers.  I can't access the code right now (I'm playing an online game...no pausing), but basically it's like this:

Code: ags
struct dlgUnit {
  char text[200];
  short state;
  };

struct dlgStruct {
  dlgUnit opt[30];
  };

dlgStruct dlg[300]; /* these are the dialogs */
dlgUnit* dlgOptOnLabel[7]; /* shown options -- doesn't work */

dlgOptOnLabel[0] = dlg[39].opt[24]; /* shown option -- doesn't work */

dlgOptOnLabel[0].state = -1; /* turn off option stored in top label via pointer -- doesn't work */


I hope this makes it clear what I'm trying to accomplish.  I want to be able to access the shown members of a dialog via a pointer vs having to test every time that I want to use it which ones are stored.

strazer

Quote
struct dlgStruct {
  dlgUnit opt[30];
};

Structs within structs aren't supported anyway, so that pointer won't help you there.

How about something like this:

Code: ags


#define NUMTOPICS 500
#define NUMOPTIONS 30

struct dlgUnit {
  char text[200];
  short state;
};

dlgUnit dlg[15000]; // 2d array: NUMTOPICS * NUMOPTIONS
int dlgOptOnLabel[7];

dlgOptOnLabel[0] = (39 * NUMOPTIONS) + 24; // position of option in 2d array

dlg[ dlgOptOnLabel[0] ].state = -1;


Of course, you better put some of this in custom functions so it's more intuitive to use.

monkey0506

With the dlgUnit struct in the header file and the dlgStruct struct in the global script it works just fine.  As for your suggestion of using an integer, I could do that, but it makes it more difficult and more confusing to operate on the data member, i.e., instead of:

Code: ags
dlgOptOnLabel[0].state = -1;


I now have to type:

Code: ags
dlg[dlgRunning].opt[dlgOptOnLabel[0]].state = -1;


That works, I was just hoping there was a better alternative...

And as for "structs within structs [not being] supported" things like arrays weren't "supported" for a long time, but a great many people still used them...as long as it works, then it should be fine...right???

strazer

#5
QuoteWith the dlgUnit struct in the header file and the dlgStruct struct in the global script it works just fine.

Hm, interesting. I thought they wouldn't work at all:

Quote from: Pumaman on Mon 28/02/2005 21:23:33structs inside structs are not currently supported.

SSH

Quote from: monkey_05_06 on Tue 12/04/2005 16:59:51
With the dlgUnit struct in the header file and the dlgStruct struct in the global script it works just fine. 

And as for "structs within structs [not being] supported" things like arrays weren't "supported" for a long time, but a great many people still used them...as long as it works, then it should be fine...right???

Hang on, you're saying you can have structs within structs? It works? I think strazer was saying that structs inside structs didn't work at all (not just not supported) ... becuase that was my understanding. Has CJ added a sekrit feature?
12

monkey0506

#7
I made a simple game (default template, plus the following lines, plus one background image saved as room1.crm) with the following (minimized) code:

Code: ags
// Main header script /* ... */ scripts.

/* struct to be used within a struct */
struct inner {
  char text[200]; /* string to be displayed from global script */
  short shortVal; /* short variable to be displayed from global script */
  };


Code: ags
// main global script file

/* struct to implement inner struct */
struct outer {
  inner something[5]; /* five inner struct types */
  };

outer test[3]; /* three outer types which implement 5 inner types */

string text2; /* text to display the short value */

/* in game_start */
  StrCopy(test[0].something[1].text, "test text");
  test[2].something[3].shortVal = -3;
  StrFormat(text2, "%d", test[2].something[3].shortVal);

/* in btnIconAbout_Click */
  Display(test[0].something[1].text);
  Display(text2);


And, it worked too!  I don't know why I chose to implement arrays here too, but it's not really necessary.  It does show that you can use them though...So, in short, I'd say, yes, this is a new "sekrit feature" (SSH).  Nice too, I might add, as it saved me over 60 lines of scripting...

[Edit:  Just to make sure that I'm clear on this...]

Please realize that the struct to be used inside of the other struct must be in the script header while the struct to contain this one must be in the global script.  If you don't do this it won't compile!  Also, just to be sure, recognize that the struct in the header file is independent of the "outer" struct, and it can be used as such...

monkey0506

Quote from: Pumaman on Wed 13/04/2005 19:50:13
Quote from: monkey_05_06The problem is, I either need a pointer to a custom struct type, or I have to make duplicate copies of the same data.

Rather than using a pointer to a custom struct, you can create an array of structs and just store the array index. It's a bit messy compared to having a pointer, but you don't need to duplicate any data.

I actually wouldn't have to duplicate the data as I thought before (as you can't assign a value to struct types themselves)...but I can't use an array either.  I'm already using an array:  "dlg[dialog].opt[option]" is the address that I would want to store in a statement like:

Code: ags
dlgOptOnLabel[0] = dlg[dialog].opt[option];


Since this won't work instead of a fool-proof call like:

Code: ags
dlgOptOnLabel[0].state = -1;


I now have to use a more error-prone call:

Code: ags
dlg[dlgRunning].opt[dlgOptOnLabel[0]].state = -1;


The problem is that with the second bit of code not only is it longer, but there is an array value that has to be set that if improperly set could cause the writer (FYI this code is for the scrolling dialog workaround template update) to change the wrong value...It works, but I was just hoping there was a more "fool-proof" alternative so that the person using the code wouldn't have to worry about accessing the wrong dialog.

I actually wouldn't have to duplicate the data as I thought before (as you can't assign a value to struct types themselves)...

strazer

Quote
I was just hoping there was a more "fool-proof" alternative so that the person using the code wouldn't have to worry about accessing the wrong dialog.

Instead of letting the user directly modify the array, I think the best way would be to provide custom functions in which you can check the parameters first.

In AGS v2.7, this could be done with a script module and static functions:

Code: ags

// module script

static function ScrollingDialog::SetOption(int dialog, int option, bool enabled) {

  if (dialog > NUMTOPICS) AbortGame("Invalid dialog specified: '%d'.", dialog);
  //...more checks

  //...the code

}


Code: ags

// module header

struct ScrollingDialog {
  import static function SetOption(int dialog, int option, bool enabled);
  //...more stuff
}

monkey0506

#10
The entire reason for the arrays existance is for it to be modified...I could provide a function for doing this, but for now I'm just trying to get the system

(a) back up to its previous functionality.
(b) add the functionality of multi-line text.

If I did add functions (which I probably will eventually) then I would do this later once these two objectives were met first anyway.  Right now I'm just trying to get the system working.  Then I can worry about the specifics like member functions, protecting data members, etc.  But then this has nothing to do with AGS 2.7 any more, so I'll just stop talking now.

Edit:  Heh...When I do add this I can even add an actually option-off-forever member...

enum dlgState {
  on,
  off,
  offByScrolling,
  offForever
  }

Lol...

RickJ

Monkey, I have to agree with Strazer that the best thing to do is to implement this as a module and use accessor functions to do all the work.   It looks to me like the puppose of dlgUnit is to implement a string array.  You could just as easily implement a string array using  a single char array (much larger of course) in the dlgStruct structure.  Just my two cents worth.  Hope it's helpful.


monkey0506

How could I use a single char array?  I need 30 strings of 200 characters.  If I tried emulating a multidimensional array when trying to access one of the 30 strings I would only be accessing a single char.  I really don't see how this would be possible.  I did have something like:

Code: ags
struct dlg {
  char text0[200];
  char text1[200];
  char text2[200];
  /* ... */
  char text29[200];
  }


but that's very long, and it's hard to access a variable data member, i.e., if I wanted to run through all of the text# strings I had to call a custom function (GetOptText(int opt)) which was defined as:

Code: ags
function GetOptText(int opt) {
  if (opt > 30) opt -= DialogVariable(0);
  if (opt == 0) return dlg[dlgRunning].text0;
  else if (opt == 1) return dlg[dlgRunning].text1;
  else if (opt == 2) return dlg[dlgRunning].text2;
  /* ... */
  else if (opt == 29) return dlg[dlgRunning].text29;
  }


I implemented the struct within a struct to make it

(a)  easier to define.
(b)  easier to call (especially from a while loop).
(c)  save a crapload of code.

I will be adding the member function once I get the system working (as previously stated), so that's not currently a concern.  It will be, just not right now...

RickJ

There are two ways I can think of.  In both cases you need a big char buffer.

char buf[6000];  // 200x30

The first mnethod is to store strings in the buf using a null terminator (i.e. buf=0;).  Then to access the array you would simply walk through the buf and count the number of nulls until the count is equal to the index of the string you wish to access.  This method makes the best use of memory space but perhaps gets a bit hairy if you plan on doing a lot of insertion and/or deltion of array elelments. 

In the second method you woiuld just allocate a fixed number of bytes to each array element.  For example lets use 200 bytes for each element.  In this case array element 3 would start at buf[600] and end at buf[799].


monkey0506

Well that seems completely illogical...Why would I choose to split every string into individual characters and store them as such?  I could just as easily emulate a multidimensional array through the use of a struct within a struct.  I don't mean to seem rude, but that seems much more complicated to do this:

Code: ags
char buf[6000];
string opt;
StrCopy(opt, "This is the first option of the second dialog.");
short i = 600;
short j = StrLen(opt);
string text;
while ((i - 600) <= j) {
  buf[i] = StrGetCharAt(text, (i - 600));
  i++;
  }


Than:

Code: ags
StrCopy(dlg[dialog].opt[option].text, "This is the first option of dialog two.");


Not to mention the fact that I would have to rebuild the string to store it on the label...I just don't see why that would appeal to me.

RickJ

Well if you put it in a script module and made accessor functions then it would be easy for anyone to pick this up and use it.   Struct within a struct isn't supported because (my understanding) of pointer management and garbage collection issues.   Although defining one struct in the header and the other in the global script appear to work at the moment it is not guranteed that it will work in a future version nor is it guranteed to not cause problems in the current version. 

You of course may do whatever you want.  I understood you to be asking for alternative methods for what you are trying to do so I took the time to explain how I would approach such a problem.  If you find my approach useful then feel free to  use it. 

SMF spam blocked by CleanTalk