CBSTR StringBuilder Class

Started by Paul Squires, July 09, 2016, 11:45:45 PM

Previous topic - Next topic

José Roca

#45
For functions like these ones, that can potentially perform many concatenations, I would use CWSTR internally for speed and return the result as a CBSTR.


' ========================================================================================
' Returns a copy of a string with characters or strings removed.
' If cbMatchStr is not present in cbMainStr, all of cbMainStr is returned intact.
' This function is case-sensitive.
' Usage example:
' DIM cbs AS CBSTR = AfxRemove("[]Hello[]", "[]")
' MessageBoxW 0, cbs, "", MB_OK
' ========================================================================================
PRIVATE FUNCTION AfxRemove (BYREF wszMainStr AS WSTRING, BYREF wszMatchStr AS WSTRING) AS CBSTR
   DIM cws AS CWSTR = wszMainStr
   DO
      DIM nPos AS LONG = INSTR(cws, wszMatchStr)
      IF nPos = 0 THEN EXIT DO
      cws.DelChars nPos, LEN(wszMatchStr)
   LOOP
   FUNCTION = cws.Str
END FUNCTION
' ========================================================================================

' ========================================================================================
' Returns a copy of a string with characters or strings removed.
' If cbMatchStr is not present in cbMainStr, all of cbMainStr is returned intact.
' cbMatchStr specifies a list of single characters to be searched for individually,
' a match on any one of which will cause that character to be removed from the result.
' This function is case-sensitive.
' Usage example:
' Removing all "b", "a", and "c"
' DIM cbs AS CBSTR = AfxRemoveAny("abacadabra", "bac")
' MessageBoxW 0, cbs, "", MB_OK
' ========================================================================================
PRIVATE FUNCTION AfxRemoveAny (BYREF wszMainStr AS WSTRING, BYREF wszMatchStr AS WSTRING) AS CBSTR
   DIM cws AS CWSTR = wszMainStr
   DIM i AS LONG
   FOR i = 1 TO LEN(wszMatchStr)
      cws = AfxRemove(cws, MID(wszMatchStr, i, 1))
   NEXT
   FUNCTION = cws.Str
END FUNCTION
' ========================================================================================


But, of course, you decide...

James Fuller

Jose,
  Why are you using BSTR allocation for the third CWstr.Insert??
James

José Roca

Because it is required by MultiByteToWideChar.

James Fuller

I really should not post to any forum before I have my second cup of coffee :)

James

James Fuller

Jose,
In my Dlg2Cwin demo I did not change the FbString input "sLine" but used all the new AfxStrxxxx functions successfully for parsing.
Is there any problem using the Fb String type except for an anticipated speed penalty?

James


Paul Squires

Quote from: Jose Roca on July 12, 2016, 02:47:20 AM
Quote
IIRC, there is a large default capacity buffer (16K) so you might want to make the default somewhat smaller. I know that it can be overloaded but most times we won't bother doing that and 16K per string seems a bit over done.

I forgot your remark. What size do you suggest?

You know that when calling the API functions most of the times we have to specify the size. The advantage of using this type (CWSTR) is that the length can be specified dynamically, not at compile time like WSTRINGs. As you know, one of the nastier problems with null terminated strings is that when we don't know the size in advance we have to allocate a buffer with, e.g. Allocate, CAllocate, etc., instead of using a WSTRING. With CWSTR we can even use a variable to especify the size.


I suggest something more reasonable like 2K or 4K. When you use CWSTR in any of your AfxStr routines then you can probably make a more reasonable judgement of what to set the Capacity of the buffer to. If the programmer is going to use the CWSTR for large concatenations then he should be smart enough to set the initial Capacity to 16K or higher. Granted, I have not done any speed tests on this so a default Capacity of 2K or 4K might be quite acceptable for the majority of cases. I fear that leaving the default as 16K for every CWSTR created seems a bit wasteful.
Paul Squires
PlanetSquires Software

José Roca

> Is there any problem using the Fb String type except for an anticipated speed penalty?

Like creating a black hole that will swallow the universe? Of course, not :)

Paul Squires

I have started using CBSTR and the new AfxStr functions. Wow! Now my code is starting to look BASIC again. Yeah! No more pointers and dereferencing. I am loving it so far and I will report any problems should I encounter them.
Paul Squires
PlanetSquires Software

Paul Squires

I have to hand it to you Jose, what you've created here is nothing short of amazing. Just before the Portugal/France match on Sunday I starting writing a new "toy" that I will show off to you guys very soon. I am so impressed by it and how easy development has been using your classes and helper functions. I just tested the application using 144 DPI and it looks amazing...and all I had to do was change one number in my code...no changing system settings or anything. Bravo!
Paul Squires
PlanetSquires Software

James Fuller

Paul,
  I echo your sentiments!!!
I am also amazed at the compiler's ability to do conversions.
I can pass fb Strings to AfxStrTally with no whining from the compiler and get the correct count!!!

James


Paul Squires

Quote from: James Fuller on July 12, 2016, 12:48:41 PM
I can pass fb Strings to AfxStrTally with no whining from the compiler and get the correct count!!!

I just experienced the same thing with AfxStrParse and AfxStrRemove. Awesome!

One thing I noticed is that Val() complains of ambiguous overload so you need to use ** double indirection.

      ' Parse to get the red,green,blue values
      r = Val( **AfxStrParse(sData, 1) )
      g = Val( **AfxStrParse(sData, 2) )
      b = Val( **AfxStrParse(sData, 3) )

Paul Squires
PlanetSquires Software

José Roca

#56
I have overloaded the += and &= operators to allow appending using cws += <some text> and &= <some text> instead of cws.Add <some text>.

Same for CBSTR, that will call this new Append function:


' ========================================================================================
' Appends a string to the BSTR. The string can be a literal or a FB STRING or WSTRING variable.
' ========================================================================================
SUB CBStr.Append (BYREF wszStr AS CONST WSTRING)
   CBSTR_DP("CBSTR Append")
   DIM n1 AS UINT = SysStringLen(m_bstr)
   DIM nLen AS UINT = .LEN(wszStr)
   IF nLen = 0 THEN EXIT SUB
   DIM b AS AFX_BSTR = SysAllocStringLen(NULL, n1 + nLen)
   IF b = NULL THEN EXIT SUB
   memcpy(b, m_bstr, n1 * SIZEOF(WSTRING))
   memcpy(b + n1, @wszStr, nLen * SIZEOF(WSTRING))
   IF m_bstr THEN SysFreeString(m_bstr)
   m_bstr = b
END SUB
' ========================================================================================


I have reduced the buffer to 4 kb, that is the size of the new maximum path length in Windows 10.

José Roca

Quote from: TechSupport on July 12, 2016, 12:35:22 PM
I have to hand it to you Jose, what you've created here is nothing short of amazing. Just before the Portugal/France match on Sunday I starting writing a new "toy" that I will show off to you guys very soon. I am so impressed by it and how easy development has been using your classes and helper functions. I just tested the application using 144 DPI and it looks amazing...and all I had to do was change one number in my code...no changing system settings or anything. Bravo!


I've improved the PowerBASIC version, in which I haven't worked since Bob's death. No worth doing it since everybody uses DDT these days.

José Roca

#58
Quote from: TechSupport on July 12, 2016, 01:11:52 PM
Quote from: James Fuller on July 12, 2016, 12:48:41 PM
I can pass fb Strings to AfxStrTally with no whining from the compiler and get the correct count!!!

I just experienced the same thing with AfxStrParse and AfxStrRemove. Awesome!

One thing I noticed is that Val() complains of ambiguous overload so you need to use ** double indirection.

      ' Parse to get the red,green,blue values
      r = Val( **AfxStrParse(sData, 1) )
      g = Val( **AfxStrParse(sData, 2) )
      b = Val( **AfxStrParse(sData, 3) )



The reason is that the ones that don't work don't generate intermediate temporary strings. Therefore, the cast operator of the class isn't called and they don't know what to do.

This is the operator that returns a BYREF AS WSTRING, that is the kind of parameter that the FB operators expect.


' ========================================================================================
' Returns a pointer to the BSTR
' ========================================================================================
OPERATOR CBStr.CAST () BYREF AS WSTRING
   OPERATOR =  *CAST(WSTRING PTR, m_bstr)
END OPERATOR
' ========================================================================================


A function like MID generates a temporary string that forces the creation of a temporary CBSTR that stores it and casts it. This means that MID is slower than LEFT and RIGHT.

VAL does not generate a temporary string and, therefore, also complains. If we force the creation of a temporary string, it works, e.g.


DIM cbs AS CBSTR = "12345"
print VAL(MID(cbs, 1))


Of course, it is much faster to use **, even if James hates it.

José Roca

#59
Quote
I have to hand it to you Jose, what you've created here is nothing short of amazing. Just before the Portugal/France match on Sunday I starting writing a new "toy" that I will show off to you guys very soon. I am so impressed by it and how easy development has been using your classes and helper functions. I just tested the application using 144 DPI and it looks amazing...and all I had to do was change one number in my code...no changing system settings or anything. Bravo!


I have posted a bunch of wrapper functions that deal with paths: http://www.planetsquires.com/protect/forum/index.php?topic=3894.0

I will also add more functions to the existing include files. I have been delaying the ones that return an string.