Munair wrote:Usually, if we don't want a parameter to be changed, or indicate that it doesn't change, we pass it as const. But we would have to give that up if we want to access the paramter by means of pointer arithmatic, as demonstrated in this thread. It's a choice.
???
Still not well understood where is the problem.
For protecting the initial string ("foo bar"), but allowing pointer arithmetic:
sub Example(p as const zstring ptr)
print *p
'p[0] = "z" '' normally disallowed
P += 4
print *p
end sub
dim t as const string = "foo bar"
dim p as const zstring ptr
Example(strptr(t))
Example(t)
p = strptr(t)
print *p
'p[0] = "z" '' normally disallowed
p += 4
print *p
Why for example want to pass to a procedure a string 'byref as const string' if you want to modify it in the body of the procedure?
Instead, pass the string 'byref as string'.
I wouldn't exactly call it a problem. There's just a trying to figure out how things work in FreeBASIC.
When translating Pascal code there are some notable differences. One important difference is Pascal's built-in type PChar, which would be translated to FreeBASIC as zstring ptr. But as you have shown, there are different possibilities when declaring a zstring ptr. One could declare a const zstring ptr a zstring const ptr or a simple zstring ptr depending on the programming needs. In Pascal this isn't necessary as the compiler is perfectly fine with a pointer to constant strings and normal strings alike using the same syntax. No matter what, the pointer is of the same type, making pointer arithmatic easy. When trying to mimic such a pointer type in FreeBASIC, type casting will be inevitable, but like I said, I'm perfectly fine with that. It's not a problem, just a figuring out what syntax to use.
fxm wrote:Why for example want to pass to a procedure a string 'byref as const string' if you want to modify it in the body of the procedure?
Instead, pass the string 'byref as string'.
Here is a short example to show the difference (I prefer to go for the second approach):
type pchar as zstring ptr
function Example1(byref s as const string) as string
dim as const zstring ptr p1
dim as zstring ptr p2
dim as string result
result = s
' different pointer types
p1 = strptr(s)
p2 = strptr(result)
'...
return result
end function
function Example2(byref s as const string) as string
dim as pchar p1, p2
dim as string result
result = s
' same pointer types
p1 = cast(pchar, strptr(s))
p2 = strptr(result)
'...
return result
end function
Somehow C and C++ have a different definition of const than FB appears to have. In C, const pointer types refer to the constant nature of the data pointed to, not the pointer itself. Furthermore const pointers in C do not have to be initialized on declaration. You are free to assign to a const pointer variable at any time, and perform pointer arithmetic. And indeed why would you not be able to do this with const pointers. Const refers to the data type that the pointer is pointing to, not the pointer itslelf. This seems a lot more logical to me how the examples here illustrate it works in FB.
I would consider FB's behavior of const pointers a bug. The following should be completely legal:
function Example3(byref s as string) as string
dim as const zstring ptr p1 = strptr(s)
dim as zstring ptr p2 = strptr(s)
dim as string result
'...
' p1[0] = "z" '' cannot modify the zstring through p1
p2[0] = "z" '' can modify the zstring through p2
'...
return result
end function
I wonder if there is a real need to have a pointer not allowing to write to a const string. After all, pointers dive under the hood, so to speak. While FB's pointer behaviour may seem logical with the different declaration possibilities, it makes things more complicated than necessary; any programmer assigning pointers is supposed to know what he's doing. Moreover, since it is possible to simply override the write-protection of a const string, why bother allowing protection control to a pointer? Yet, even though this override is allowed, it results in what I would call unexpected behaviour when writing a string byte. Writing the same byte as ubyte seems ok:
dim as const string s = "foo bar"
dim as const zstring ptr p1
dim as zstring ptr p2
'pointer to const string
p1 = strptr(s)
' override const
p2 = cast(zstring ptr, strptr(s))
'error, pointer knows string is const:
'p1[2] = "t"
'truncates string to "fot" when printing *p2
p2[2] = "t"
'inserts character: "fot bar" when printing *p2
'(*p2)[2] = asc("t")
print *p2 'prints "fot" ??? (unexpected)
print s 'prints "fot bar" (expected)
sleep
end
Ideally, a pointer would allow read/write access to memory regardless of a buffer's type. It would make things easier and wouldn't result in inconsistencies as demonstrated here.
'p2[2]' is a zstring, so 'p2[2] = "t"' writes a zstring from the 3rd character of the 's' string character data : the '"t"' character plus the 'chr(0)' character (the string termination character).
This 'chr(0)' character is taken into account as terminal character only when dereferencing the 'zstring ptr', not when accessing the 's' string.
(only a var-len string accepts the nul character as any other character, because the string length is set in the descriptor)
Munair wrote:Moreover, since it is possible to simply override the write-protection of a const string, why bother allowing protection control to a pointer?
You are right, but it is really a very special case:
Sub test (Byval p As Zstring Ptr)
*p = "Y"
End Sub
Dim As Const String s = "X"
print s
'test(strptr(s)) '' not OK
test(s) '' OK
Print s
(only an easy way for interfacing with C functions)
Munair wrote:Ideally, a pointer would allow read/write access to memory regardless of a buffer's type. It would make things easier and wouldn't result in inconsistencies as demonstrated here.
It's your opinion, it's not mine. I consider the above behavior rather like a very small exception to the rule.
Last edited by fxm on Dec 08, 2017 19:19, edited 1 time in total.
fxm wrote:'p2[2]' is a zstring, so 'p2[2] = "t"' writes a zstring from the 3rd character of the 's' string character data : the '"t"' character plus the 'chr(0)' character (the string termination character).
This 'chr(0)' character is taken into account as terminal character only when dereferencing the 'zstring ptr', not when accessing the 's' string.
(only a var-len string accepts the nul character as any other character, because the string length is set in the descriptor)
Which means that the safe way - or rather the only way - to write a single byte is by ubyte when planning to dereference a 'zstring ptr'.
For example, with the current code I'm working on: (*ostr)[0] = asc("i")
caseih wrote:Somehow C and C++ have a different definition of const than FB appears to have. In C, const pointer types refer to the constant nature of the data pointed to, not the pointer itself. Furthermore const pointers in C do not have to be initialized on declaration. You are free to assign to a const pointer variable at any time, and perform pointer arithmetic. And indeed why would you not be able to do this with const pointers. Const refers to the data type that the pointer is pointing to, not the pointer itslelf. This seems a lot more logical to me how the examples here illustrate it works in FB.
That is not true. In C/C++ it works exactly as it works in FB: You can declare either a constant pointer or a pointer to a constant or both (or nothing).
// pointer to constant int:
const int * dummy;
// constant pointer to int:
int * const dummy = 0;
// constant pointer to constant int:
const int * const dummy = 0;
So FB behaves as it should and there's no bug.
A const qualifier specifies that a pointer or value is constant. If a pointer is specified to be constant any modification is disallowed and of course this also include pointer arithmetics. If you still need to do that on a constant pointer you're very likely doing something wrong (or the pointer shouldn't be constant). Same for the constant values: If the value that a pointer points to is specified to be constant it cannot be modified. If you still want to do this you should check your code as again probably something's wrong. Casting the const types to non-const types is considered bad style as you are working around the of the intension of the const qualifiers.