There is no const member function equivalent in VB.
All it's saying is the property is read only / doesn't modify the objects data.
Agree, that is why I thought making the VB STATIC would resolve the issue.
Found this interesting Circular Buffer concept in C++ (CodeProject - see Source link in Class in my zip) and am trying to convert to VB.
Attached includes original as well as my VB attempt to date.
Anyone who wants to assist feel free.
Last edited by vb6forever; Oct 24th, 2018 at 06:05 PM.
There's no real equivalent of const, nor does there necessarily need to be. Even in C, you could "cast away const", so the word was really just a suggestion to the compiler which it might make use of, or not, to generate improved code. Since you could get rid of it in C, you can get rid of it in VB.
passel: Thanks for responding and solution.
My error on "overloaded operator" in this case.
Can't explain why I marked it such.
FWIW here is a discussion on overloaded operators. https://en.cppreference.com/w/cpp/language/operators
yeah, I don't see any overloaded operators in that class.
What I do see is a lot of pointer math.
You might want to handle the sign bit specifically.
Code:
Private Function PtrAdd(ByVal Address As Long, ByVal Offset As Long) As Long
' unsigned pointer arithmetic, moves overflow by toggling the sign bit
' required when using /LARGEADDRESSAWARE on 64bit windows
Const SIGN_BIT As Long = &H80000000
PtrAdd = (Address Xor SIGN_BIT) + Offset Xor SIGN_BIT
End Function
Last edited by DEXWERX; Oct 22nd, 2018 at 11:46 AM.
Also in C/C++ NULL or null is typically a pointer value of zero.
It's nothing like Null in VB which is more in line with the Database concept of a null value.
What's worse is that you've used the Constant vbNull which is a Variant Type... used with VarType() or the like.
It's really just another way of checking if a Variant contains Null, instead of using IsNull etc.
You could try a variation of this instead.
Code:
Private Enum API
FALSE_
TRUE_
NULL_ = 0&
[False] = 0
[True] = 1
[Null] = 0
End Enum
Code:
?vbNull
1
?API.Null
0
NOTE VB also has Nothing and vbNullString which are both essentially zero pointers.
Last edited by DEXWERX; Oct 23rd, 2018 at 11:23 AM.
So the deal with LARGEADDRESAWARE is that if you enable the flag in your 32bit VB6 exe, and run it on 64bit windows, it will have access to 4GB (minus memory used for shared O/S resources, and process dlls). The only caveat of using this flag, is that any pointer math that doesn't use unsigned integers can overflow when crossing the 2GB boundary. This includes any DLL dependencies that are not written expecting a full 4GB Address space, they may throw a random error.
you can edit the vbp file and manually add the following to enable it.
Code:
[VBCompiler]
LinkSwitches= /LARGEADDRESSAWARE
Anyway - back to the original question:
Code:
int GetCommittedSize() const
really ends up translating to
Code:
Public Property Get CommittedSize() As Long
Using a property is VB's way of hinting that you're not modifying members, where a function hints at side effects.
Of course it's only a convention and, similar to const, is easily subverted.
Translation was also simple, because all the math was organized using offsets and sizes - so the only pointer math needed is when returning a final calculated pointer. What you do with those returned pointers in VB is another story. I use an AryMap class to access arbitrary locations as an Array. I'll leave that up the the end users of the class.
Code:
'https://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist
'/*
' Copyright (c) 2003 Simon Cooke, All Rights Reserved
'
' Licensed royalty-free for commercial and non-commercial
' use, without warranty or guarantee of suitability for any purpose.
' All that I ask is that you send me an email
' telling me that you're using my code. It'll make me
' feel warm and fuzzy inside. spectecjr@gmail.com
'
'*/
Option Explicit
Private Enum API
FALSE_
TRUE_
NULL_ = 0
#If False Then
Dim FALSE_, TRUE_, NULL_
#End If
End Enum
Private Declare Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Const MEM_COMMIT As Long = &H1000&
Private Const MEM_DECOMMIT As Long = &H4000&
Private Const PAGE_READWRITE As Long = &H4&
Private Type SYSTEM_INFO
dwOemId As Long
dwPageSize As Long
lpMinimumApplicationAddress As Long
lpMaximumApplicationAddress As Long
dwActiveProcessorMask As Long
dwNumberOfProcessors As Long
dwProcessorType As Long
dwAllocationGranularity As Long
wProcessorLevel As Integer
wProcessorRevision As Integer
End Type
Private Declare Sub GetSystemInfo Lib "kernel32" (ByRef lpSystemInfo As SYSTEM_INFO)
Private pBuffer As Long
Private ixa As Long
Private sza As Long
Private ixb As Long
Private szb As Long
Private buflen As Long
Private ixResrv As Long
Private szResrv As Long
Private Sub Class_Terminate()
'// We don't call FreeBuffer, because we don't need to reset our variables - our object is dying
If pBuffer Then VirtualFree pBuffer, buflen, MEM_DECOMMIT
End Sub
Public Function AllocateBuffer(Optional ByVal BufferSize As Long = 4096&) As Boolean
'// Allocate Buffer
'//
'// Allocates a buffer in virtual memory, to the nearest page size (rounded up)
'//
'// Parameters:
'// int buffersize size of buffer to allocate, in bytes (default: 4096)
'//
'// Returns:
'// bool true if successful, false if buffer cannot be allocated
If BufferSize <= 0 Then Exit Function
If pBuffer Then FreeBuffer
Dim si As SYSTEM_INFO
GetSystemInfo si
' Calculate nearest page size
BufferSize = ((BufferSize + si.dwPageSize - 1) \ si.dwPageSize) * si.dwPageSize
pBuffer = VirtualAlloc(NULL_, BufferSize, MEM_COMMIT, PAGE_READWRITE)
If pBuffer = NULL_ Then Exit Function
buflen = BufferSize
AllocateBuffer = True
End Function
Public Function Clear()
'///
'/// \brief Clears the buffer of any allocations.
'///
'/// Clears the buffer of any allocations or reservations. Note; it
'/// does not wipe the buffer memory; it merely resets all pointers,
'/// returning the buffer to a completely empty state ready for new
'/// allocations.
'///
ixa = 0
sza = 0
ixb = 0
szb = 0
ixResrv = 0
szResrv = 0
End Function
Public Sub FreeBuffer()
'// Free Buffer
'//
'// Frees a previously allocated buffer, resetting all internal pointers to 0.
'//
'// Parameters:
'// none
'//
'// Returns:
'// void
If pBuffer = NULL_ Then Exit Sub
ixa = 0
sza = 0
ixb = 0
szb = 0
buflen = 0
VirtualFree pBuffer, 0&, MEM_DECOMMIT
pBuffer = NULL_
End Sub
Public Function Reserve(ByVal Size As Long, ByRef Reserved As Long) As Long
'// Reserve
'//
'// Reserves space in the buffer for a memory write operation
'//
'// Parameters:
'// int size amount of space to reserve
'// OUT int& reserved size of space actually reserved
'//
'// Returns:
'// BYTE* pointer to the reserved block
'//
'// Notes:
'// Will return NULL for the pointer if no space can be allocated.
'// Can return any value from 1 to size in reserved.
'// Will return NULL if a previous reservation has not been committed.
Dim FreeSpace As Long
'// We always allocate on B if B exists; this means we have two blocks and our buffer is filling.
If szb Then
FreeSpace = BFreeSpace()
If Size < FreeSpace Then FreeSpace = Size
If FreeSpace = 0 Then Exit Function
szResrv = FreeSpace
Reserved = FreeSpace
ixResrv = ixb + szb
Reserve = PtrAdd(pBuffer, ixResrv)
Else
'// Block b does not exist, so we can check if the space AFTER a is bigger than the space
'// before A, and allocate the bigger one.
FreeSpace = SpaceAfterA()
If FreeSpace >= ixa Then
If FreeSpace = 0 Then Exit Function
If Size < FreeSpace Then FreeSpace = Size
szResrv = FreeSpace
Reserved = FreeSpace
ixResrv = ixa + sza
Reserve = PtrAdd(pBuffer, ixResrv)
Else
If ixa = 0 Then Exit Function
If ixa < Size Then Size = ixa
szResrv = Size
Reserved = Size
ixResrv = 0
Reserve = pBuffer
End If
End If
End Function
Sub Commit(ByVal Size As Long)
'// Commit
'//
'// Commits space that has been written to in the buffer
'//
'// Parameters:
'// int size number of bytes to commit
'//
'// Notes:
'// Committing a size > than the reserved size will cause an assert in a debug build;
'// in a release build, the actual reserved size will be used.
'// Committing a size < than the reserved size will commit that amount of data, and release
'// the rest of the space.
'// Committing a size of 0 will release the reservation.
'//
If Size = 0 Then
szResrv = 0
ixResrv = 0
Exit Sub
End If
' // If we try to commit more space than we asked for, clip to the size we asked for.
If Size > szResrv Then Size = szResrv
' // If we have no blocks being used currently, we create one in A.
If sza = 0 And szb = 0 Then
ixa = ixResrv
sza = Size
ixResrv = 0
szResrv = 0
Exit Sub
End If
If ixResrv = sza + ixa Then
sza = sza + Size
Else
szb = szb + Size
End If
ixResrv = 0
szResrv = 0
End Sub
Public Function GetContiguousBlock(ByRef Size As Long) As Long
'// GetContiguousBlock
'//
'// Gets a pointer to the first contiguous block in the buffer, and returns the size of that block.
'//
'// Parameters:
'// OUT int & size returns the size of the first contiguous block
'//
'// Returns:
'// BYTE* pointer to the first contiguous block, or NULL if empty.
If sza = 0 Then
Size = 0
Exit Function
End If
Size = sza
GetContiguousBlock = PtrAdd(pBuffer, ixa)
End Function
Public Sub DecommitBlock(Size As Long)
'// DecommitBlock
'//
'// Decommits space from the first contiguous block
'//
'// Parameters:
'// int size amount of memory to decommit
'//
'// Returns:
'// nothing
If Size >= sza Then
ixa = ixb
sza = szb
ixb = 0
szb = 0
Else
sza = sza - Size
ixa = ixa + Size
End If
End Sub
Public Property Get CommittedSize() As Long
'// GetCommittedSize
'//
'// Queries how much data (in total) has been committed in the buffer
'//
'// Parameters:
'// none
'//
'// Returns:
'// int total amount of committed data in the buffer
CommittedSize = sza + szb
End Property
Public Property Get ReservationSize() As Long
'// GetReservationSize
'//
'// Queries how much space has been reserved in the buffer.
'//
'// Parameters:
'// none
'//
'// Returns:
'// int number of bytes that have been reserved
'//
'// Notes:
'// A return value of 0 indicates that no space has been reserved
ReservationSize = szResrv
End Property
Public Property Get BufferSize() As Long
'// GetBufferSize
'//
'// Queries the maximum total size of the buffer
'//
'// Parameters:
'// none
'//
'// Returns:
'// int total size of buffer
BufferSize = buflen
End Property
Public Property Get IsInitialized() As Boolean
'// IsInitialized
'//
'// Queries whether or not the buffer has been allocated
'//
'// Parameters:
'// none
'//
'// Returns:
'// bool true if the buffer has been allocated
IsInitialized = pBuffer <> NULL_
End Property
Private Property Get SpaceAfterA() As Long
SpaceAfterA = buflen - ixa - sza
End Property
Private Property Get BFreeSpace() As Long
BFreeSpace = ixa - ixb - szb
End Property
Private Function PtrAdd(ByVal Address As Long, ByVal Offset As Long) As Long
' unsigned pointer arithmetic, moves overflow by toggling the sign bit
' required when using /LARGEADDRESSAWARE on 64bit windows
Const SIGN_BIT As Long = &H80000000
PtrAdd = (Address Xor SIGN_BIT) + Offset Xor SIGN_BIT
End Function
PtrAdd handles the case when Pointer addition crosses a 31bit boundary. This matters because VB6 doesn't have an unsigned 32bit type. A Long's MSB is a sign bit, which means the max value is 2GB. If you have a pointer to a high address stored in a Long, that would end up >2GB when adding an offset - you will overflow the long type. Unless you have this turned off in the compiler options - you'll throw an overflow exception and most likely crash. This actually doesn't matter for 32bit processes without LAA - because windows will only give it a 2GB address space. Theoretically you'll never have an address >2GB in this case.
Conversely using PtrAdd in a 32bit process without LAA - doesn't change the final computed pointer. It just gives you more range for the case that does (LAA on a 64bit OS) If you're only dealing with unsigned values <2GB, there is no difference interpreting 32bit pointer values signed or unsigned. You can use PtrAdd in both cases with the only penalty being extra bit toggling (and a function call if you don't inline the operation).
This whole issue only applies to 32bit processes, it does not address any issues with 64bit. If you want to handle the 64bit VBA case - you actually would use the LongPtr type which should be unsigned. Don't use PtrAdd which assumes bit 32 is a sign bit.
edit: The .NET analog would be IntPtr.Add()
Last edited by DEXWERX; Oct 29th, 2018 at 07:18 AM.
@vb6forever: With signed integers you can cross the zero boundary w/o any troubles e.g. ptr = -100, offset = 500 => ptr + offset is 400 (1) and everything is fine around zero boundary. The runtime implements an overflow check around the 2^31 boundary e.g. ptr = &H7FFFFFFF - 100, offset = 500 => pre + offset raises an exception (2).
The Xor SIGN_BIT trick swaps cases (1) and (2) i.e. PtrAdd(-100, 500) raises overflow error while PtrAdd(&H7FFFFFFF - 100, 500) = &H8000018F is correctly calculated unsigned addition.