Announcement

Collapse
No announcement yet.

Possible bug in PowerBASIC's implementation of Fixed-length strings

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Michael Fitzpatrick
    replied
    Hi Bob:
    Thanks for your very helpful explanation. I will be studying this entire thread over the next week or so. I see that I am still on the lower portion of the learning curve. May you have a blessed day.

    Sincerely,

    Michael D Fitzpatrick

    Leave a comment:


  • Gösta H. Lovgren-2
    replied
    Originally posted by Michael Fitzpatrick View Post
    Hi Bob:
    I am having some difficulty understanding your post.
    1) When I tried compiling just this much in PowerBASIC:
    DIM xx$(1 TO 4)
    xx(1) = "xc"
    xx(2) = "SR"
    xx(3) = "DI"
    xx(4) = "CO"
    I got the compiler message: "Missing declaration: xx"
    Michael Fitzpatrick
    Michael,

    You don't show your actual code used (always preferred on this forum) so it's difficult to determine exactly what the problem is. I'd bet you have a "#Dim All" at the top of your code (Highly reccoed for all code). Try
    Code:
    '...
    Local xx$()  '<<<<Add this befire Dimming **************
    Dim (xx$(1 to 4)
    '...
    And then run your program.


    ===============================================
    "It is better to be feared than loved,
    if you cannot be both."
    Niccolo Machiavelli (1469-1527), "The Prince"
    ===============================================

    Leave a comment:


  • Michael Mattias
    replied
    Not that diversion is necessarily a bad thing, but I think we may have forgotten the 'conditions of contest' here...

    The problem is NOT to "do something" in PowerBASIC to allow a PB-written function in a DLL to understand what has been passed to it; the problem is finding the best way to express and work with whatever VBA is passing to that DLL-based function.

    When you control both sides (the passer and the passee, so to speak) many things are possible... but when you don't control one side or the other, the number of things which can be made to work tends to be somewhat more limited.

    MCM

    Leave a comment:


  • John Gleason
    replied
    For more clarity, here is a compilable example:
    Code:
    #COMPILE EXE
    #DIM ALL
    
    FUNCTION PBMAIN () AS LONG
      LOCAL msgString AS STRING
      
      msgString = STRING$(200, ".")  '50 elements * 4 bytes per element = 200 bytes needed
      DIM IOfcArry(49) AS LONG AT STRPTR(msgString) 'overlay array onto msgString. (again, rather advanced technique)
      
         IOfcArry(0) = 25464    '"xc" = &h6378 = 25464. &h63 = "c" and &h78 = "x" but stored reversed (aka little endian).
         IOfcArry(1) = 5392708  '"DIR"
         IOfcArry(2) = 21075    '"SR"
         '. . .
         IOfcArry(49) = 5197635 '"COO"
         REPLACE ANY $NUL WITH $SPC IN msgString 'gets rid of $NUL characters so we can print full string
       ? msgString              'now you can see how your strings are stored properly
    END FUNCTION

    Leave a comment:


  • John Gleason
    replied
    For example, the string array whose elements are:
    OfcArry(0) = "xc"
    OfcArry(1) = "DIR"
    OfcArry(2) = "SR"
    . . .
    OfcArry(49) = "COO"

    can instead be represented by an array of integers whose elements are:
    IOfcArry(0) = 4
    IOfcArry(1) = 1
    IOfcArry(2) = 2
    . . .
    IOfcArry(49) = 3
    Going back to your idea above and Bob's subsequent suggestion: Like you say, the integers will be faster. But rather than using 1 thru 4 to represent your strings, because the strings are short--3 characters max--they can be immediately converted beforehand to their 4-byte capable LONG equivalents and likely save a re-conversion step later. The IOfcArr() elements become:
    Code:
    IOfcArry(0) = 25464    '"xc" = &h6378 = 25464. &h63 = "c" and &h78 = "x" but stored reversed (aka little endian). 
    IOfcArry(1) = 5392708  '"DIR"
    IOfcArry(2) = 21075    '"SR"
    . . .
    IOfcArry(49) = 5197635 '"COO"
    The IOfcArr() now holds the exact characters of the strings but will be processed in the fastest way as LONG's. I'm with Gösta on this one being a rather advanced technique and hopefully this doesn't sound too obtuse.

    Leave a comment:


  • Bob Zale
    replied
    I didn't create code which was intended to be used anywhere in particular. I just answered your question and gave you some example pseudo-code to demonstrate the principles.

    DIM xx$(...) creates, allocates, and initializes a dynamic string array named xx$(). There is no exclusivity to the name, so you must reference it with the "$" type identifier to tell it apart from xx%() and xx#() and others.

    DIM xx(...) AS STRING creates, allocates, and initializes a dynamic string array. Additionally, the AS STRING declares that the name xx is exclusive to this particular name and will not be used for another array in this scope and context. It can then be referenced without a type identifier as xx(). Now, xx() and xx$() may be used interchangeably. They refer to the same array data, bevause you told the compiler that any use of the ambiguous name xx() should be interpreted as the dynamic string array named by xx().

    The variables x& and y& are long integers. VARPTR() and STRPTR() return a numeric value which tells you the location (address) of a variable and string data respectively. An address is a numeric value, not a string, so the address must be assigned to a numeric variable.

    You asked me where in memory a particular set of data may be found. The concept of "where" requires a numeric answer because that is the way an address (location) is represented. The code I gave you showed a generic way to obtain the address of a variable or string data.

    Bob Zale
    PowerBASIC Inc.

    Leave a comment:


  • Michael Fitzpatrick
    replied
    Missing Declaration

    Hi Bob:
    I am having some difficulty understanding your post.
    1) When I tried compiling just this much in PowerBASIC:
    DIM xx$(1 TO 4)
    xx(1) = "xc"
    xx(2) = "SR"
    xx(3) = "DI"
    xx(4) = "CO"
    I got the compiler message: "Missing declaration: xx"
    Did you mean DIM xx(1 TO 4) As String?
    And did you mean for this DIM to reside in a PowerBASIC source or in an Excel VBA procedure (as per my situation)?
    2) If we are dealing with arrays of strings, why would you have:
    x& = PEEK(LONG, VARPTR(xx$(1)))
    y& = STRPTR(xx$(1))
    Wouldn't x& and y& be of type LONG? (Perhaps I am missing something here.) How would those two statements (and any other relevant code) have to be modified to deal with strings instead of integers? Thanks for your kind help. May you have a blessed day.
    Sincerely,
    Michael Fitzpatrick
    Last edited by Michael Fitzpatrick; 4 Apr 2009, 10:29 PM. Reason: typo

    Leave a comment:


  • Bob Zale
    replied
    Hi Michael,

    Assuming,,,

    DIM xx$(1 TO 4)

    This creates an array of four dynamic string variables. These four string variables are stored in contiguous memory locations. Each of these variables consists of of a 32-bit (4-byte) pointer to the actual string data. If the string is nul (zero-length) the pointer is zero (0).

    VARPTR(xx$(1)) returns the address (location) of that string variable. The next 3 array members follow in memory.

    STRPTR(xx$(1)) returns the address of the string data, or zero if nul.

    STRPTR(xx$(1)) is equal to the pointer at VARPTR(xx$(1))

    x& = PEEK(LONG, VARPTR(xx$(1)))
    y& = STRPTR(xx$(1))
    x& and y& are always equal.

    Best regards,

    Bob Zale
    PowerBASIC Inc.

    Leave a comment:


  • Michael Fitzpatrick
    replied
    Hi Bob:
    I was wondering: In your March 28th 5 PM post you state: "A 4 element array is four of those pointers which could point anywhere, because each could be any length, including nul, in which case the pointer would be zero. Effectively, you have string pointers with two levels of indirection." Is there any place in memory where those 4 pointers are in contiguous locations? Thanks! May you have a blessed day.
    Sincerely,
    Michael D Fitzpatrick

    Leave a comment:


  • Gösta H. Lovgren-2
    replied
    Originally posted by Michael Mattias View Post
    I guess you don't use the CVx/MKx$ functions as much as you did since UDTs replaced Ye Olde "FIELD" statements used in older BASIC to access numbers stored in disk files, but they can still be useful functions to have around.

    MCM
    No, you are right. Haven't used them (in Field) in a long time. Dunno why/how I could forget. Started with Fields back when it was a max of 255 bytes and records had to be a multiple of 255 (5 51 byte records, for example of "efficient" storage or later data divisible by 255). (First on tape storage then using 60k capacity floppies so compacting was important). (The proper term is Boundry - Just reminded of that when reading another thread,)

    Thanks the reminder, M. A year or so ago when John G and I were playing around with the Word Connection puzzle solver (Programming Forum), he was using packed strings for the data and this dummy didn't make the Mk/Cv connection then about packing strings. Now I see it clearly. Or maybe I don't. Close enough anyway.

    ==================================
    "The difference between
    'involvement' and 'commitment'
    is like a ham-and-eggs breakfast:
    the chicken was 'involved'
    the pig was 'committed'."
    unknown
    ==================================
    Last edited by Gösta H. Lovgren-2; 29 Mar 2009, 08:46 PM. Reason: "You have to set Rules *Boundries* and Leemitations." - Cesar Milan

    Leave a comment:


  • Michael Mattias
    replied
    It is for anyone who hasn't used it. Perhaps you would care to post an actual example demostrating how to use it.
    MKL$() allows you to assign the four bytes of a long integer to a string variable
    CVL() allows you to assign the value of any four bytes to a long integer variable

    Code:
      LOCAL  pL AS LONG PTR, L AS LONG, S AS STRING 
    
      LET L =  12345&
      LET S  =  MKL$(L) 
      LET   pl =  STRPTR (S)    ' point to the string 
      MSGBOX  @Pl
    If you want to look at it another way, a UNION is a good example. Four bytes is four bytes... you can look at those four bytes as a string or a long integer.
    Code:
    UNION LongUnion
      L   AS LONG
      S   AS STRING * 4 
    END UNION
    
       LOCAL U AS LongUnion 
       U.S  = MKL$(12345&) 
       MSGBOX   FORMAT$(U.L)
    I guess you don't use the CVx/MKx$ functions as much as you did since UDTs replaced Ye Olde "FIELD" statements used in older BASIC to access numbers stored in disk files, but they can still be useful functions to have around.

    MCM

    Leave a comment:


  • Gösta H. Lovgren-2
    replied
    Originally posted by Michael Mattias View Post
    It is?
    It is for anyone who hasn't used it. Perhaps you would care to post an actual example demostrating how to use it.

    =========================================================
    "Perfect love is rare indeed
    for to be a lover will require that you continually have:
    the subtlety of the very wise,
    the flexibility of the child,
    the sensitivity of the artist,
    the understanding of the philosopher
    the acceptance of the saint,
    the tolerance of the scholar
    and the fortitude of the certain."
    Leo Buscaglia
    =========================================================

    Leave a comment:


  • Bob Zale
    replied
    Michael--

    It would normally be something like:

    LongVarArr(0) = CVL("xc" + CHR$(0,0))

    However, if you have to go through a special group of assignment statements like this, I probably wouldn't waste the time here. I'd most likely put any extra code on the PowerBASIC side, as it will likely be much faster. I just tossed this out as a general idea which might help... never know about all the circumstances.

    Best regards,

    Bob

    Leave a comment:


  • Michael Mattias
    replied
    Bob was talking about packing strings into integers first (a fairly sophisticated programming process
    It is?

    For a two byte string, X% = CVI(string)
    For any string up to four bytes, X& = CVL(LEFT$(string + SPACE$(4)), 4)

    (SPACE$() can be $NUL or anything else, just make sure CVL() gets an argument of at least four characters).

    To convert back to strings in called function, MKI$() or MKL$() can be used.

    Leave a comment:


  • Gösta H. Lovgren-2
    replied
    Michael, you are getting a type mismatch error because:

    Originally posted by Michael Fitzpatrick View Post

    Code:
     
    Dim LongVarArr(0 To 4) As Long [COLOR=red]'Array defined as Long Ingeger (a number)[/COLOR]
    LongVarArr(0) = "xc"                [COLOR=red]'Assignig a string to an integer[/COLOR]
    Bob was talking about packing strings into integers first (a fairly sophisticated programming process) so a simpler numeric array can get passed rather than a string array.

    =================================
    When your only tool is a hammer,
    everything looks like a nail.
    (FREDH - Salon's Table Talk)
    =================================

    Leave a comment:


  • Michael Fitzpatrick
    replied
    Re Bob Zale's 8:12 PM suggestion

    Hi Bob:
    Thanks for the suggestion. I tried to implement it in the following Excel VBA code which will call the DLL to display the various values of the LongVarArr Array. Unfortunately when I tried to compile this Excel VBA code, the compiler gave me the message: "Type mismatch". I clicked the debug button of the message, and "LongVarArr(0) = "xc"" was highlighted. Apparently, Excel does not want to convert a text string to its value as a Long. How should I proceed with this code?

    Code:
     
    Declare Function ProcTxtAsLngArry Lib "C:\PBWin90\MySamples\MySample1.dll" (ByRef x&) As String
    Sub TestPassTxtAsLngArrs()
    Dim LongVarArr(0 To 4) As Long
    LongVarArr(0) = "xc"
    LongVarArr(1) = "SR"
    LongVarArr(2) = "DIR"
    LongVarArr(3) = "COO"
    LongVarArr(4) = "xc"
    Dim AnswStr As String
    AnswStr = ProcTxtAsLngArry(LongVarArr(0))
    MsgBox "AnswStr = " & AnswStr
    End Sub
    Thanks again for your help.

    Sincerely,

    Michael Fitzpatrick

    Leave a comment:


  • Michael Fitzpatrick
    replied
    Your suggestion

    Thanks for your suggestion. Since my motive here is speed, I will have to compare the execution time of packing the data (in Excel VBA which is interpreted, not compiled) and then unpacking it in my DLL with the time to pass an array of integers (already formed in Excel) to a DLL which then utilizes that array. Thanks again for your suggestion. May you have a blessed day.

    Sincerely,

    Michael Fitzpatrick

    Leave a comment:


  • colin glenn
    replied
    Could you not pass it as a delimiter'ed string? That is, join all the elements into one string with, say $TAB's between the elements and then just break it back apart when your DLL gets it?

    Leave a comment:


  • Bob Zale
    replied
    If all of your string data is 4 bytes or less, don't use a numeric value which merely "represents" the string. Instead, use a Long Integer which is equal to the 4 bytes or less of string data. That totally eliminates the conversion step.

    Leave a comment:


  • Michael Fitzpatrick
    replied
    Passing string arrays from Excel to a PowerBASIC created DLL

    Greetings! I thank everyone of you for your responses. My main purpose in creating the DLL was to take some slow-running interpreted code from an Excel VBA module and place it in a DLL which could run that code much faster, the code in a DLL being compiled rather than interpreted. Therefore my main objectives are speed, speed, and speed.
    Now here is my bright idea for essentially transferring string information from Excel to a DLL which will process it. Since I have only 4 different types of strings: "xc", "COO", "SR" and "DIR", I will let these strings be represented by integers as follows:

    Code:
     
    String             Use this Integer Representation
    "xc"                              4
    "COO"                             3
    "SR"                              2
    "DIR"                             1
    For example, the string array whose elements are:
    OfcArry(0) = "xc"
    OfcArry(1) = "DIR"
    OfcArry(2) = "SR"
    . . .
    OfcArry(49) = "COO"

    can instead be represented by an array of integers whose elements are:
    IOfcArry(0) = 4
    IOfcArry(1) = 1
    IOfcArry(2) = 2
    . . .
    IOfcArry(49) = 3

    and I believe that transferring this array of 50 integers from Excel to the PowerBASIC DLL is probably much faster than any of the schemes suggested or hinted at in this thread. Of course, if anyone has a faster and/or simpler scheme than this one, I will be happy to adopt it. May you have a blessed day.

    Sincerely,

    Michael Fitzpatrick

    Leave a comment:

Working...
X