Page 1 of 1

Procedures that return pointers to structures

Posted: Fri Jul 16, 2004 12:32 am
by PolyVector
It would be nice if you wanted to return a structure if it was treated as a pointer to the struct....
This would be far more realistic than having the function return a copy of the struct (why do people even want that?)

Code: Select all

Structure Player
  Name$
EndStructure

Global GoodGuy.Player
Global BadGuy.Player

GoodGuy\Name$="PolyVector"
BadGuy\Name$="Robotnik"

Procedure.Player RandomPlayer()
  If Random(100)>50
    ProcedureReturn GoodGuy
  Else
    ProcedureReturn BadGuy
  EndIf
EndProcedure

Debug RandomPlayer()\Name$
RandomPlayer()\Name$="RandomGuy"

Posted: Fri Jul 16, 2004 4:48 am
by Codemonger
couldn't you pass the address of the structure using the '@' ... or are you saying it should be more like .net, where every object is implicitly considered to be used or passed by reference or memeory address and not by value. Anyway not sure i understand ... funny code though

Posted: Fri Jul 16, 2004 7:16 am
by PolyVector
This way of doing things is pretty standard in C++ with the & (reference) symbol...
It can make for very dynamic code if used correctly... Anyways, it was only a suggestion....

Code: Select all

int x;

int &MyX()
{
  return x;
}

void main()
{
  MyX()=10;
  cout << MyX();
  return;
}
Maybe *Procedure would be a better way to declare it?
Oh well...

Posted: Fri Jul 16, 2004 9:26 am
by GedB
What your code is really doing is returning a pointer to the variable. I don't like the way that it does this, because it is hidden behind some cryptic syntax.

Memory management is a nightmare, because the pointer can remain after the original value has been deleted.

In purebasic if you want to return a pointer, then you return a pointer: a long value that is exactly what it says it is. This helps you stay aware of how things really are.

Given your example code, if you call RandomPlayer() 100 times you still only have 2 instances of Player.

Take, for example, this code (valid pb, you can run it)

Code: Select all

Structure Player
  Name$
EndStructure

Global GoodGuy.Player
Global BadGuy.Player

GoodGuy\Name$="PolyVector"
BadGuy\Name$="Robotnik"

Procedure.l RandomPlayer()
  If Random(100)>50
    ProcedureReturn @GoodGuy
  Else
    ProcedureReturn @BadGuy
  EndIf
EndProcedure

DefType.Player *Player1, *Player2, *player3, *player4
*Player1 = RandomPlayer()
*Player2 = RandomPlayer()
*player3 = RandomPlayer()
*player4 = RandomPlayer()

Debug "Player1: " + *Player1\Name$
Debug "Player2: " + *Player2\Name$
Debug "Player3: " + *player3\Name$
Debug "Player4: " + *player4\Name$

*Player1\Name$="RandomGuy1"
*Player2\Name$="RandomGuy2"
*player3\Name$="RandomGuy3"
*player4\Name$="RandomGuy4"

Debug "Player1: " + *Player1\Name$
Debug "Player2: " + *Player2\Name$
Debug "Player3: " + *player3\Name$
Debug "Player4: " + *player4\Name$
Run it a couple of times and see how the result differ?

Is this the results you expected? I can't see them being worth much.

Tracing the problem is easier with PB. We are reminded that we are dealing with a pointer by the need to prefix the variable with an asterix and the functions declaration shows that it is returning a long, not a Player. We the C++ style all of this is hidden from us, an experienced programmer has to step away from the code an figure out what is really going on.

Quickly change the code so that it creates a copy of GoodGuy or BadGuy and returns that. Does this fix the problem? It creates new problems. How are you going to keep track of the memory used by each player? When will you free it? If you're writing a game that throws countless players at the user you could be creating thousands of players which only last a few seconds before being shot by the player. When will you reclaim their memory?

You can make this work, by dealing with this and dealing with that and the conclusion is inevitable: Objects. For objects to work properly you need garbage collection. Your code just got slower and your executable bigger.

You can add feature after feature to the language to help manage all of this, and you end up with an impenetrably complex language like C++.

PB takes a different path, it stays honest with the programmer and doesn't pretend to make difficult things simple. This is how it earns our respect.

There are a couple of solutions for this problem, and you choose which one based on your own needs keeping full control over all of it. I would favour:

Code: Select all

Structure Player
  Name$
EndStructure

Procedure RandomPlayer(*Player.Player)
  If Random(100)>50
    *Player\Name$ = "PolyVector"
  Else
    *Player\Name$ = "Robotnik"
  EndIf
EndProcedure

DefType.Player Player1, Player2, player3, player4
RandomPlayer(Player1)
RandomPlayer(Player2)
RandomPlayer(player3)
RandomPlayer(player4)

Debug "Player1: " + Player1\Name$
Debug "Player2: " + Player2\Name$
Debug "Player3: " + player3\Name$
Debug "Player4: " + player4\Name$

Player1\Name$="RandomGuy1"
Player2\Name$="RandomGuy2"
player3\Name$="RandomGuy3"
player4\Name$="RandomGuy4"

Debug "Player1: " + Player1\Name$
Debug "Player2: " + Player2\Name$
Debug "Player3: " + player3\Name$
Debug "Player4: " + player4\Name$
There are other methods, depending upon you needs. The good thing is it is easy to understand the tradeoffs you are making because everything is kept simple and out in the open.

Posted: Fri Jul 16, 2004 10:25 am
by PolyVector
@GedB
I impliment pointers in insanely complex ways in my skin engine, FreeStyle... From my experience, this syntax would make my life a lot easier... I was suggesting this as merely a level of transparency/shortcut....

It would mean that this:

Code: Select all

Procedure.l GetPlayer()
    ProcedureReturn PointerToSomePlayer
EndProcedure

*GoodGuy.Player
*GoodGuy=GetPlayer()
Debug *GoodGuy\Name$
...could be written like this:

Code: Select all

Procedure.Player GetPlayer()
    ProcedureReturn PointerToSomePlayer
EndProcedure

Debug GetPlayer()\Name$
I wouldn't consider this to be 'cryptic syntax'... To me it is far more readable :D
If it clearifies things to use the *Pointer symbol... Then I would suggest This:

Code: Select all

Procedure.Player *GetPlayer()
    ProcedureReturn PointerToSomePlayer
EndProcedure

Debug *GetPlayer()\Name$

Posted: Fri Jul 16, 2004 11:12 am
by GedB
Anything that encourages or assists the insanely complex use of pointers must be resisted. :twisted:

..

Posted: Fri Jul 16, 2004 2:45 pm
by NoahPhense
GedB wrote:Anything that encourages or assists the insanely complex use of pointers must be resisted. :twisted:
lol

@pv
If Random(100)>50

Keep in mind that 0 is a factor of rnd(100)

If Random(102)>50 ; 0-50 = 51 & 51-102 = 51 ;)


*It's *good @to *see @people talking, @*about Pointers.

PS - anyone care to explain this one..

The @* <-- in the code below .. ive never seen it used before, but
apparently its working for the guy..

Code: Select all

*iError.w = 0 
GetState.w = 0 
Toggle.w =0 

; there is also AttachProcess() DetachProcess() 
; ..for your future adventures 

ProcedureDLL AttachThread(Instance) 
  OpenLibrary (0,"vcmain32.dll") 
EndProcedure 

ProcedureDLL HidePoints() 
  CallFunction (0,"VCSetPointDisplay",@*iError,0)  
EndProcedure 

ProcedureDLL ShowPoints() 
  CallFunction (0,"VCSetPointDisplay",@*iError,1) 
EndProcedure 

ProcedureDLL TogglePoints() 
  GetState.w = CallFunction (0,"VCGetPointDisplay", @iError) 
  Toggle.w = 1 - GetState.w 
  CallFunction (0,"VCSetPointDisplay",@*iError,Toggle.w) 
EndProcedure 

ProcedureDLL DetachThread(Instance) 
  CloseLibrary(0) 
EndProcedure
- np

Posted: Fri Jul 16, 2004 3:08 pm
by Fred
It' to get the address of the pointer. The pointer is a long variable (.l) so you can get its address as any other variable.

..

Posted: Fri Jul 16, 2004 3:13 pm
by NoahPhense
Fred wrote:It' to get the address of the pointer. The pointer is a long variable (.l) so you can get its address as any other variable.
Nifty, thanks Fred.

- np