• Welcome to Jose's Read Only Forum 2023.
 

FreeBASIC CWstr

Started by Juergen Kuehlwein, April 09, 2018, 11:39:00 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

José Roca

I also added the sptr method, that does the same that a single *, for those used to STRPTR that forget that a CWSTR is not a string, but a TYPE, and that STRPTR can only be used with strings, not with TYPEs. With a CWSTR you don't need to use STRPTR because it has an overloaded cast operator (OPERATOR CWstr.CAST () AS ANY PTR) that allows you to pass the CWSTR variable directly to a procedure that expects a pointer to the string data, but some expect to work with it exactly as with the natively supported strings types, which is not 100% possible.


José Roca

#61
Quote
If we return a CWstr as CWstr, the data is still valid, even if the local CWstr goes out of scope, because we return it as CWstr. But then some FreeBASIC commands complain of an "invalid data type" ..

When you return a CWSTR, CBSTR, STRING, WSTRING or string literal in a function that has a CWSTR as the return type, the compiler creates a temporary CWSTR and calls its appropriate constructor. It is important to remember that you must use RETURN <CWSTR or CBSTR> instead of FUNCTION = <CWSTR or CBSTR>. The reason is that when we use RETURN the constructor is called BEFORE the local variable goes out of scope, whereas if we use FUNCTION =, the constructor of the temporry CWSTR to be returned is called AFTER the local variable has been destroyed, making impossible to copy it. I don't know if this beavior of FUNCTION = is a bug or intentional (if it is intentional, it doesn't make sense to me).

The problem was that I had not implemented a copy operator (see below).

José Roca

I have done further tests and FUNCTION = works fine. Maybe when I did the first tests I had not yet implemented the copy operator because I didn't know how exactly FUNCTION = worked. I'm glad that it was my mistake and not a bug.

Juergen Kuehlwein

Well José,


i will put this aside for the moment, and direct my focus on the integration of my VD with FreeBASIC again.


But i´m, pretty sure there is a solution for avoiding "**" or ".wstr" somehow or other. Look at this code:



#compiler freebasic
#compile console 32 exe

#define unicode
#include once "windows.bi"
#include once "Afx\AfxStr.inc"
#define USTRING CBSTR


declare function FB_MAIN as uinteger


END FB_MAIN


'***********************************************************************************************
'***********************************************************************************************


function returnbyref(u as ustring) byref as wstring
'***********************************************************************************************
'
'***********************************************************************************************
static i as long
static a(1 to 10) as ubyte ptr


  i = i + 1
  if i > 10 then i = 1

  if a(i) > 0 then
    deallocate a(i)
  end if

  a(i) = u.m_pBuffer


  function = *cast(WSTRING PTR, u.m_pBuffer)
  u.m_pBuffer = 0                                     'prevent buffer from beeing deallocated


end function


'***********************************************************************************************


PRIVATE FUNCTION left2 overload (BYREF w AS WSTRING, BYVAL n AS LONG) byref AS Wstring 'const wstring
'***********************************************************************************************
' return the leftmost n chars of w
'***********************************************************************************************
dim u as ustring = left(w, n)


  return returnbyref(u)


END FUNCTION


'***********************************************************************************************


FUNCTION FB_MAIN AS uinteger
'***********************************************************************************************
' main
'***********************************************************************************************
dim i as long
dim n as long
dim s as string        = "1234" 
dim w as Wstring * 64  = "56789"
dim u as Ustring       = "abcdef"
dim x as Ustring     


  SELECT CASE LEFT2(s, 2)           
    case "12"
      print "ok"
  end select

  sleep


  x = left2(s, 1) + left2(s, 2) + left2(s, 3)
  print x
  x = left2(w, 1) + left2(w, 2) + left2(w, 3)
  print x
  x = left2(u, 1) + left2(u, 2) + left2(u, 3)
  print x
  sleep


end function



It pools the pointers to the CWstr data and prohibits the the data to be deallocated, when the CWstr goes out of scope and deallocates it later. I know this not a serious solution for many reasons. E.g. it fails, if there are too many pointers to be be stored simultaneously, but it gives an idea what could be done. I have yet another idea for a completely different approach, but this will require some research - maybe later.


Another point - if i define a CBstr as USTRING, the "DelChars" function is missing when using the string helper functions (AfxStrRemove, etc.). I know, YOU don´t need this, but would it be possible to add it (DelChars to CBstr), so i could have both:

#define USTRING CWstr
#define USTRING CBstr

As far as i can see, it´s the only one missing to make it interchangeable


Thanks


JK

José Roca

They're easily converted from one to another, but they are not interchangeable. Redefining CWSTR to CBSTR, or viceversa, with defines is likely to cause many problems. I think that it is better that you add overloaded functions to work with your UISTRING.

Juergen Kuehlwein

Hi José,


it´s me again ...


Working with what i find in "AfxStr.inc", i think i found some problems. I put it together in this code:



'console 32

#include "afx\afxstr.inc"


'dim t as CWstr = "ФЫВЙЦУФЫ"                           'fails for "any"
'dim t as CBstr = "ФЫВЙЦУФЫ"                           'fails for "any"
dim t as wstring * 64 = "ФЫВЙЦУФЫ"                    'fails for "any"

print str(AfxStrTallyAny(t, "ФЫ"))                    'fails for wide strings (returns 0)
print str(AfxStrTally(t, "ФЫ"))                       'this works


'dim t1 as Zstring * 10 = "asdfgas"
dim t1 as string * 10 = "asdfgas"                     
print str(AfxStrTallyAny(t1, "as"))                   'this works
print str(AfxStrTally(t1, "as"))                      'this works


print str(AfxStrParseCountAny(t1, "as"))              'returns 3 - should return 5 ?


t = "aЙЦäУКЙЦöööУКЙЦУК123üüüü45"
messageboxw 0, "-" + afxstrshrink(t, " äöü") + "-", "Error", 0
messageboxw 0, "-" + afxstrshrink(t, " Й") + "-", "Error", 0
messageboxw 0, "-" + afxstrreverse(t) + "-", "Error", 0



The more, i wrote own functions for sting manipulation in FreeBASIC mostly using your code with small changes ("fb_str.inc" in "Ustring.zip" attachment). You must add a "#define USTRING Afx.CBstr" and it makes use of some constants defined in "fb.inc" e.g "any_".

Woud you please be so kind as to review it or even test it with your methods, so i can be sure, that it is bug free ? The basic idea is making it possible to have an implementation of your CWstr (defined as USTRING) and string helper functions for it (and all other FreeBASIC string types) even, if WINFBX is not present or intentionally isn´t used. When WINFBX is included, USTRING gets defined as CBstr and all WINFBX functionality can be used without interfering with my aditions (which are always available nevertheless).

You should not use my IDE for testing, because the latest public version still has some inconsistencies regarding FreeBASIC. There will be an update fixing this, but i would like to include the attached code and i want to be as sure as possible, that it works properly.


Thanks,


JK 

José Roca

Seems to be a problem with INSTR when using a CWSTR variable instead of **. This works:


PRIVATE FUNCTION AfxStrTallyAny (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS WSTRING) AS LONG
   IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN EXIT FUNCTION
   ' // Remove possible duplicates in the matches string
   DIM nPos AS LONG
   DIM cwsMatchStr AS CWSTR = wszMatchStr
   FOR i AS LONG = 1 TO LEN(cwsMatchStr)
      nPos = INSTR(**cwsMatchStr, MID(wszMatchStr, i, 1))
      IF nPos = 0 THEN cwsMatchStr += MID(wszMatchStr, i, 1)
   NEXT
   ' // Do the count
   DIM nCount AS LONG
   FOR i AS LONG = 1 TO LEN(cwsMatchStr)
      nPos = 1
      DO
         nPos = INSTR(nPos, wszMainStr, MID(**cwsMatchStr, i, 1))
         IF nPos = 0 THEN EXIT DO
         IF nPos THEN
            nCount += 1
            nPos += 1
         END IF
      LOOP
   NEXT
   RETURN nCount
END FUNCTION


I will revise the string functions that use INSTR.

José Roca

#67
I have modified the string functions that used cws instead of **cws.

Juergen Kuehlwein

Hi José,


i can download the attachment, but 7zip cannot open it. What did you use for creating it ?


JK

José Roca

I used RAR. New attachment with .zip extension.

José Roca

#70
Your mid_ function is buggy. Must be if n = 0 instead of <> 0.


PRIVATE FUNCTION mid_ (BYREF w AS WSTRING, BYVAL i AS ULONG, n AS ULONG = 0) AS ustring
'  if n <> 0 then
  if n = 0 then
    return mid(w, i)
  else
    return mid(w, i, n)
  end if
end function 


BTW if I were you I will always add explicit BYVAL or BYREF to the parameters; otherwise, you will get many warnings when compiling with the -w pedantic option.

Juergen Kuehlwein

Yep, was meant the other way round! Thanks for the .zip


JK

José Roca

Changed AfxStrParseCountAny  to be consistent with PB's PARSECOUNT:


PRIVATE FUNCTION AfxStrParseCountAny (BYREF wszMainStr AS CONST WSTRING, BYREF wszDelimiter AS WSTRING = ",") AS LONG
   DIM nCount AS LONG = 1
   FOR i AS LONG = 1 TO LEN(wszDelimiter)
      nCount += AfxStrParseCount(wszMainStr, MID(wszDelimiter, i, 1))
   NEXT
   RETURN nCount
END FUNCTION
[code]

José Roca

When using Replace_, the contents of the orginal string are sometimes changed.


DIM ustr AS USTRING = "1234567890"
print Replace_(ustr, "5", "x")
print ustr


Same behavior with Insert_.

José Roca

Also with Remove_.


DIM ustr AS USTRING = "1234567890"
print Remove_(ustr, "23")
print ustr


Looks like these are side effects of defining a USTRING as a CBSTR instead of a CWSTR. I told you that they were'nt interchangeable. Changing the define from #define USTRING Afx.CBstr to #define USTRING Afx.CWstr works fine.