MODULE: Stack v2.0 - Vectorized generic stacks - Updated 25 January 2015

Started by monkey0506, Sat 21/03/2009 01:10:47

Previous topic - Next topic

Monsieur OUXX

I'd like to devise a simple way to make this module as easy as possible to adapt to any custom struct.
(Note 1: by "any custom struct" I'm not talking about the ones you black-flagged, i.e. the ones that have a temporary life, like File* and such)
(Note 2: I'm not talking about the universal stack "Stack", which aims at storing all types at once, but more something like StringCache_t, which aims at storing one specific type at a time. Only then, why not, include that manually in "Stack" if necessary, to make it even more universal)

For example:
- In the module, I'd put a #define CUSTOMTYPE AnyCustomType
- Everywhere in the module, you (Monkey) only always use CUSTOMTYPE as argument to functions, or return value, etc.
- Then the end-user can import this script as many times as he wants (under a different name each time, e.g. MyStruct1Stack, MyStruct2Stack, etc.)  ...
- ...And in each import, he only changes the define line to #define CUSTOMTYPE MyStruct1, or #define CUSTOMTYPE MyStruct2, etc.
(I think this would be possible only thanks to the newer #define that can overwrite the previous definition each time you redefine it -- or am I mistaken?)



Any dirty trick like the one I described above is welcome, the goal is simply to be able to implement as many custom stacks as possible with as little human intervention as possible.
What do you think? Simpliicty is the master word here.



===========

also, an unrelated question: why did you define Grow and set_Capacity as extenders?
 

monkey0506

I've deleted a rather large post that I had typed up, so please bear with me on this, but in order to be stored into a Stack instance, you have to have some way of referring back to the individual instances of your custom struct type. There are two ways of doing this:

  • Provide serialization methods for your struct to "stringify" it.
  • Make your struct a "managed struct" and use pointers to it (NOTE: this method only works if your struct does not store any pointers, including Strings).

    If you provide serialization methods, then then you do not modify the Stack functions at all:

    Code: ags
    StackData Serialize(this MyStruct*)
    {
      String buffer = String.Format("%d,%f...", this.myInt, this.myFloat);
      return StackData.StringToData(buffer);
    }
    
    bool Deserialize(this MyStruct*, StackData data)
    {
      // explode the CSV string....
      this.myInt = iValue;
      this.myFloat = fValue;
    }


    However, if your struct does not store any pointers, you can simply use extender methods to Push/Pop your type directly:

    Code: ags
    bool PushMyStack(this Stack*, MyStack *value, int index, bool insert)
    {
      bool result = this.PushInt(value.myInt, index, insert);
      result = ((result) && (this.PushFloat(value.myFloat, index + 1, insert)); // TODO: check if index is SCR_NO_VALUE first....
      // ...TODO: lazy eval if any push op fails?
      return result;
    }
    
    MyStack* PopMyStack(this Stack*, int index, bool remove)
    {
      MyStack *result = new MyStack;
      result.myInt = this.PopInt(index, remove);
      result.myFloat = this.PopFloat(index + 1, remove); // TODO: check if index is SCR_NO_VALUE or if removing items, update offset appropriately
      return result;
    }


    Quote from: Monsieur OUXX on Mon 18/01/2016 16:39:07also, an unrelated question: why did you define Grow and set_Capacity as extenders?

    Grow was implemented as an extender for simplicity and logical grouping. Simplicity in that it's simpler to not have to type the import line, and also in that it's simpler to type "this" than "StringCache". Logical grouping, meaning, keeping it grouped together with the StringCache_t type, as AGS does not have namespaces except for struct namespaces.

    set_Capacity was defined as an extender for the purpose of hiding the method. It could easily be imported by any script, but there's really no need to do so as the Capacity attribute is writable. See the wiki article on attributes, particularly, the section on extenders. The point is just to not have the function listed in the script header, and thereby not have it show up in autocomplete or be automatically imported.

Monsieur OUXX

I still can't get my head around the AGs language restrictions.
Let me make sure I understand: if my custom struct contains, let's say, a GUI*, then I'm screwed? Because:
- if I go down the "managed struct" path, then it's forbidden to use pointers,
- and if I go down the stringify road, then I cannot stringify a GUI*.
Or can I? I believe the whole purpose of your Stack struct is to not stringify the built-in types, like GUI*.
 

Crimson Wizard

Quote from: Monsieur OUXX on Tue 19/01/2016 14:24:05
- and if I go down the stringify road, then I cannot stringify a GUI*.
Or can I? I believe the whole purpose of your Stack struct is to not stringify the built-in types, like GUI*.
You could use GUI's ID number, I believe.

monkey0506

Yes, most things can be stringified in some meaningful way. If you have to store a GUI*, then you could stringify it by using the GUI.ID. If you don't mind having a separate script for your struct definition though, you could do this:

Code: ags
// header

autoptr managed struct Thing
{
  import attribute GUI *TheGUI; // attributes are still okay, because they're functions, not actual pointers!
  protected int guiID; // int defaults to 0, so this will be GUI.ID + 1 instead -- 0 will be null GUI*
  // blah
};

import bool PushThing(this Stack*, Thing thing, int index=SCR_NO_VALUE, bool insert=true);
import Thing PopThing(this Stack*, int index=SCR_NO_VALUE, bool remove=true);


Code: ags
// script

GUI* get_TheGUI(this Thing*)
{
  if (this.guiID == 0)
  {
    return null;
  }
  return gui[this.guiID - 1];
}

void set_TheGUI(this Thing*, GUI *value)
{
  if (value == null)
  {
    this.guiID = 0;
  }
  else
  {
    this.guiID = value.ID + 1;
  }
}

bool PushThing(this Stack*, Thing thing, int index, bool insert)
{
  this.PushGUI(thing.get_TheGUI(), index, insert); // inside THIS SCRIPT ONLY, we must use the accessor functions directly
  // ...
  return true; // TODO: return correct result based on whether all members were pushed
}

Thing PopThing(this Stack*, int index, bool remove)
{
  Thing thing = new Thing;
  thing.set_TheGUI(this.PopGUI(index, remove));
  // ...
  return thing;
}


Code: ags
// some other script

Thing myThing = new Thing;
myThing.TheGUI = gMyThingGUI;
Stack stack;
stack.PushThing(myThing);
// ...

Monsieur OUXX

OK thanks a lot to both of you.
Monkey, would you mind post in that other thread from yesterday where I ask the meaning of autoptr? Thanks.

EDIT: Oh, you already did.
 

Monsieur OUXX

Warning: the module does not compile in AGS 3.4 (patch 2), because the array "inventory" does not exist anymore.
I haven't found a workaround yet.
 

Crimson Wizard

Quote from: Monsieur OUXX on Mon 13/02/2017 13:07:49
Warning: the module does not compile in AGS 3.4 (patch 2), because the array "inventory" does not exist anymore.
I haven't found a workaround yet.

You must create at least 1 inventory item to make it appear. This does not relate to particular version of AGS.

We have an issue in the tracker:
http://www.adventuregamestudio.co.uk/forums/index.php?issue=683.0

monkey0506

I'm proud to announce that all versions of this module are now available on Github:

https://github.com/monkey0506/ags-module-stack/releases

Version history between minor versions is now easier to visualize, and new releases are automatically built courtesy of Travis CI. 8-)

SMF spam blocked by CleanTalk