Bjarne Stroustrup explained in a presentation how C++ gets memory safe when using a borrowing like concept as in Rust.
He also said, that beyond memory safety also files, etc. must be closed and tidied up. So garbage collection does not help here.
How would I use PureBasic pointers to stuctures or memory in a way, that I can say which part of the program is when resoponsible or
has the pointer in its area of control/responsibility?
I know that variables get freed after leaving a procedure.
But when I let a procedure create a structure for further calculation it is given from one to another procedure.
Any links to already discussed topics or any ideas really apreciated.
Thanks!
Borrow pointers; Memory Safety; Rust/C++;
Re: Borrow pointers; Memory Safety; Rust/C++;
When a procedure create structure data, there are two possibilities.
1. The data is static and is released by PB when the program is terminated.
2. The caller releases the data after use.
Example:
1. The data is static and is released by PB when the program is terminated.
2. The caller releases the data after use.
Example:
Code: Select all
Structure udtData
iVal.i
sVal.s
EndStructure
Procedure myFunctionStatic()
Static *pData.udtData
If Not *pData
*pData = AllocateStructure(udtData)
EndIf
If *pData
*pData\iVal = 100
*pData\sVal = "Hello World!"
EndIf
ProcedureReturn *pData
EndProcedure
Procedure myFunctionData()
Protected *pData.udtData
*pData = AllocateStructure(udtData)
If *pData
*pData\iVal = 200
*pData\sVal = "Hello World!"
EndIf
ProcedureReturn *pData
EndProcedure
Define *data1.udtData, *data2.udtData
*data2 = myFunctionData()
Debug *data2\iVal
*data2 = myFunctionData() ; <- MemoryLeak !!! because forget free structure
Debug *data2\iVal
FreeStructure(*data2)
*data1 = myFunctionStatic()
Debug *data1\iVal
*data1 = myFunctionStatic()
Debug *data1\iVal
FreeStructure(*data1) ; <- do not with static data
*data1 = myFunctionStatic() ; <- Crash because destroyed static data
Debug *data1\iVal
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Re: Borrow pointers; Memory Safety; Rust/C++;
Nothing that is built in today for PureBasic, however I guess it is theoretically possible to use GCC extensions with the C backend to hook into advanced features to track these kind of things, e.g. there exists a proof of concept smart pointer C library that uses this functionality to clean up resources when pointer is no longer in use.
https://github.com/Snaipe/libcsptr
What I do instead is a custom module that when in debug mode tracks memory allocations, file handlers, etc and reports if anything is still open when the application closes, primitive but works well.
Edit: I think the GCC extension in question is the __attribute__ cleanup
https://github.com/Snaipe/libcsptr/blob ... C32-L44C39
https://gcc.gnu.org/onlinedocs/gcc/Comm ... -attribute
https://github.com/Snaipe/libcsptr
What I do instead is a custom module that when in debug mode tracks memory allocations, file handlers, etc and reports if anything is still open when the application closes, primitive but works well.
Edit: I think the GCC extension in question is the __attribute__ cleanup
https://github.com/Snaipe/libcsptr/blob ... C32-L44C39
https://gcc.gnu.org/onlinedocs/gcc/Comm ... -attribute
Borrow pointers; Memory Safety; Rust/C++;
@mk-soft, thanks for the example to clarify things.
@tored, thanks a lot for the links and the info
@tored, thanks a lot for the links and the info
Is this as simple as it reads: You simply store all pointers in a list for example with 1 in use, 0 destroyed and at the end they sum up as 0 or something went wrong?What I do instead is a custom module that when in debug mode tracks memory allocations, file handlers, etc and reports if anything is still open when the application closes, primitive but works well.
Re: Borrow pointers; Memory Safety; Rust/C++;
Yes, the general idea is to override PureBasic library procedures with macros and and add whatever you are tracking to a list.HanPBF wrote: Tue Feb 27, 2024 1:07 pm Is this as simple as it reads: You simply store all pointers in a list for example with 1 in use, 0 destroyed and at the end they sum up as 0 or something went wrong?
Here is my current implementation, at the moment I only have support for memory and files, but it is easy to add whatever needed (it is thread safe). I use a module for this because all my code is module based, however the drawback is that you need to remember to do UseModule Tracker before use.
The inspiration for this came from this thread https://www.purebasic.fr/english/viewto ... 12&t=39168
After this code segment I add an example.
Code: Select all
DeclareModule Tracker
EnableExplicit
CompilerIf #PB_Compiler_Debugger
Macro AllocateStructureProxy(name, command = AllocateStructure)
command(name)
EndMacro
Macro AllocateMemory(size, flags = #Null)
Tracker::__TrackAllocateMemory(size, flags, #PB_Compiler_File, #PB_Compiler_Line)
EndMacro
Macro ReAllocateMemory(mem, size, flags = #Null)
Tracker::__TrackReAllocateMemory(mem, size, flags, #PB_Compiler_File, #PB_Compiler_Line)
EndMacro
Macro FreeMemory(mem)
Tracker::__TrackFreeMemory(mem)
EndMacro
Macro AllocateStructure(name)
Tracker::__TrackAllocateStructure(AllocateStructureProxy(name), SizeOf(name), #PB_Compiler_File, #PB_Compiler_Line)
EndMacro
Macro FreeStructure(mem)
Tracker::__TrackFreeStructure(mem)
EndMacro
Macro CreateFile(file, filename, flags = #Null)
Tracker::__TrackCreateFile(file, filename, flags, #PB_Compiler_File, #PB_Compiler_Line)
EndMacro
Macro OpenFile(file, filename, flags = #Null)
Tracker::__TrackOpenFile(file, filename, flags, #PB_Compiler_File, #PB_Compiler_Line)
EndMacro
Macro ReadFile(file, filename, flags = #Null)
Tracker::__TrackReadFile(file, filename, flags, #PB_Compiler_File, #PB_Compiler_Line)
EndMacro
Macro CloseFile(file)
Tracker::__TrackCloseFile(file)
EndMacro
Declare __TrackAllocateMemory(size.q, flags, trackFile.s, trackLine.i)
Declare __TrackReAllocateMemory(*mem, size.q, flags, trackFile.s, trackLine.i)
Declare __TrackFreeMemory(*mem)
Declare __TrackAllocateStructure(*mem, size.q, trackFile.s, trackLine.i)
Declare __TrackFreeStructure(*mem)
Declare __TrackCreateFile(file, filename.s, flags, trackFile.s, trackLine.i)
Declare __TrackOpenFile(file, filename.s, flags, trackFile.s, trackLine.i)
Declare __TrackReadFile(file, filename.s, flags, trackFile.s, trackLine.i)
Declare __TrackCloseFile(file)
CompilerEndIf
Declare PrintAllocations()
EndDeclareModule
Module Tracker
CompilerIf #PB_Compiler_Debugger
CompilerIf #PB_Compiler_Thread
Global mutex = CreateMutex()
Macro Lock()
LockMutex(mutex)
EndMacro
Macro Unlock()
UnlockMutex(mutex)
EndMacro
CompilerElse
Macro Lock()
EndMacro
Macro Unlock()
EndMacro
CompilerEndIf
Macro AllocateMemoryProxy(size, flags, command = AllocateMemory)
command(size, flags)
EndMacro
Macro ReAllocateMemoryProxy(mem, size, flags, command = ReAllocateMemory)
command(mem, size, flags)
EndMacro
Macro FreeMemoryProxy(mem, command = FreeMemory)
command(mem)
EndMacro
Macro FreeStructureProxy(mem, command = FreeStructure)
command(mem)
EndMacro
Structure MemoryAllocation
*mem
size.q
trackFile.s
trackLine.i
EndStructure
Global NewList memoryAllocations.MemoryAllocation()
Procedure TrackMemoryAllocation(*mem, size, trackFile.s, trackLine.i)
Lock()
If *mem And AddElement(memoryAllocations())
memoryAllocations()\mem = *mem
memoryAllocations()\size = size
memoryAllocations()\trackFile = trackFile
memoryAllocations()\trackLine = trackLine
EndIf
Unlock()
EndProcedure
Procedure __TrackAllocateMemory(size.q, flags, trackFile.s, trackLine.i)
Protected *mem = AllocateMemoryProxy(size, flags)
TrackMemoryAllocation(*mem, size, trackFile, trackLine)
ProcedureReturn *mem
EndProcedure
Procedure __TrackReAllocateMemory(*mem, size.q, flags, trackFile.s, trackLine.i)
Protected *new = ReAllocateMemoryProxy(*mem, size, flags)
If *new
Lock()
ForEach memoryAllocations()
If memoryAllocations()\mem = *mem
DeleteElement(memoryAllocations())
Break
EndIf
Next
Unlock()
TrackMemoryAllocation(*new, size, trackFile, trackLine)
EndIf
ProcedureReturn *new
EndProcedure
Procedure __TrackFreeMemory(*mem)
Lock()
ForEach memoryAllocations()
If memoryAllocations()\mem = *mem
DeleteElement(memoryAllocations())
Break
EndIf
Next
Unlock()
FreeMemoryProxy(*mem)
EndProcedure
Procedure __TrackAllocateStructure(*mem, size.q, trackFile.s, trackLine.i)
TrackMemoryAllocation(*mem, size, trackFile, trackLine)
ProcedureReturn *mem
EndProcedure
Procedure __TrackFreeStructure(*mem)
Lock()
ForEach memoryAllocations()
If memoryAllocations()\mem = *mem
DeleteElement(memoryAllocations())
Break
EndIf
Next
Unlock()
FreeStructureProxy(*mem)
EndProcedure
Macro CreateFileProxy(file, filename, flags, command = CreateFile)
command(file, filename, flags)
EndMacro
Macro OpenFileProxy(file, filename, flags, command = OpenFile)
command(file, filename, flags)
EndMacro
Macro ReadFileProxy(file, filename, flags, command = ReadFile)
command(file, filename, flags)
EndMacro
Macro CloseFileProxy(file, command = CloseFile)
command(file)
EndMacro
Structure FileAllocation
file.i
filename.s
trackFile.s
trackLine.i
EndStructure
Global NewList fileAllocations.FileAllocation()
Procedure TrackFileOperation(id, file, filename.s, trackFile.s, trackLine.i)
Lock()
If id And AddElement(fileAllocations())
If file = #PB_Any
fileAllocations()\file = id
Else
fileAllocations()\file = file
EndIf
fileAllocations()\filename = filename
fileAllocations()\trackFile = trackFile
fileAllocations()\trackLine = trackLine
EndIf
Unlock()
EndProcedure
Procedure __TrackCreateFile(file, filename.s, flags, trackFile.s, trackLine.i)
Protected id = CreateFileProxy(file, filename, flags)
TrackFileOperation(id, file, filename, trackFile, trackLine)
ProcedureReturn id
EndProcedure
Procedure __TrackOpenFile(file, filename.s, flags, trackFile.s, trackLine.i)
Protected id = OpenFileProxy(file, filename, flags)
TrackFileOperation(id, file, filename, trackFile, trackLine)
ProcedureReturn id
EndProcedure
Procedure __TrackReadFile(file, filename.s, flags, trackFile.s, trackLine.i)
Protected id = ReadFileProxy(file, filename, flags)
TrackFileOperation(id, file, filename, trackFile, trackLine)
ProcedureReturn id
EndProcedure
Procedure __TrackCloseFile(file)
Lock()
If LastElement(fileAllocations())
Repeat
If fileAllocations()\file = file
DeleteElement(fileAllocations())
Break
EndIf
Until PreviousElement(fileAllocations()) = #False
LastElement(fileAllocations())
EndIf
Unlock()
CloseFileProxy(file)
EndProcedure
CompilerEndIf
Procedure PrintAllocations()
CompilerIf #PB_Compiler_Debugger
Lock()
If ListSize(memoryAllocations())
Debug "-[ Allocated Memory ]------"
ForEach memoryAllocations()
Debug memoryAllocations()\trackFile +
":" + memoryAllocations()\trackLine +
" Size: " + memoryAllocations()\size +
" Address: " + memoryAllocations()\mem
Next
Debug "---------------------------"
EndIf
If ListSize(fileAllocations())
Debug "-[ Allocated Files ]------"
ForEach fileAllocations()
Debug fileAllocations()\trackFile +
":" + fileAllocations()\trackLine +
" File: " + fileAllocations()\filename +
" Id: " + fileAllocations()\file
Next
Debug "---------------------------"
EndIf
Unlock()
CompilerEndIf
EndProcedure
EndModule
Code: Select all
DeclareModule MyModule
EnableExplicit
UseModule Tracker
Declare DoStuff()
EndDeclareModule
Module MyModule
Procedure DoStuff()
Protected *mem = AllocateMemory(100)
FreeMemory(*mem)
*mem = AllocateMemory(100) ; <-- missing free
ReadFile(0,#PB_Compiler_File)
CloseFile(0)
ReadFile(0 ,#PB_Compiler_File) ; <-- missing close
EndProcedure
EndModule
MyModule::DoStuff()
Tracker::PrintAllocations() ; <-- should print two leaks
Re: Borrow pointers; Memory Safety; Rust/C++;
Can you mix PB macros and inline C?
Code: Select all
EnableExplicit
CompilerIf #PB_Compiler_Backend <> #PB_Backend_C
CompilerError "Use C backend"
CompilerEndIf
Procedure Defer(*p)
Debug "defer"
EndProcedure
Procedure DoStuff()
! int p __attribute__((cleanup(f_defer)));
Debug "do stuff"
EndProcedure
DoStuff()
End
Defer(#Null)
Re: Borrow pointers; Memory Safety; Rust/C++;

Code: Select all
EnableExplicit
Macro Defer(__deferexpression)
Define __deferloop#MacroExpandedCount
While __deferloop#MacroExpandedCount = 0 Or __deferexpression
__deferloop#MacroExpandedCount + 1
EndMacro
Macro EndDefer
Wend
EndMacro
Procedure Free(*mem)
Debug "Free: " + PeekS(*mem)
FreeMemory(*mem)
EndProcedure
Procedure Test()
Protected *mem = AllocateMemory(100)
Defer(Free(*mem))
PokeS(*mem, "Hello World")
EndDefer
EndProcedure
Test()