• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

TCLib Now Uses Msvcrt.dll

Started by Frederick J. Harris, July 07, 2016, 09:27:26 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

I get those kinds of errors all the time James.  Its always some faul up with the #define UNICODE and #define _UNICODE defines.  I've gathered you set up your builds somewhat different from mine, but that's where to look.

James C. Fuller

Fred,
  Nope it was my bad. My tchar.h file was a bit off. All's well now.

James

James C. Fuller

Fred,
  Just a quick note.
I was able to port ALL your demos to bc9Basic.
I will discuss this more on the Bc9Basic Forum Area.

Great Work!!!

James


Frederick J. Harris

James,

Regarding you recent email (hope you don't mind my answering here), this seems to work as expected.  The function strHello returns a String object...


// cl Demo30.cpp Strings.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"

String strHello()
{
return L"Hello, World!";
}

int main()
{
String s1=strHello();
s1.Print(_T("s1         = "), true);
_tprintf(_T("s1.lpStr() = %s\n"), s1.lpStr());
getchar();

return 0;
}


What happens in this case is that the line...


return L"Hello, World!";


...causes a call to one of my overloaded String Constructors which takes a NULL terminated character string as a parameter, and in that manner causes a String object to be created, which is then returned.  You can see these things if you put debug output statements in the various constructors.  When I was developing my String Class code over the years I did piles of that sort of thing, and it is indeed interesting to see all the String Class members that get called in various circumstances.  This is particularly true with the operator= and operator+ members.

The above can be abbreviated quite a bit to this, which amazingly works...


// cl Demo30.cpp Strings.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"

String strHello()
{
return L"Hello, World!";
}

int main()
{
strHello().Print(true);
getchar();

return 0;
}


So in other words, the Microsoft compiler is looking at the strHello() function, and recognizing that it returns a String object, and that being the case, it seems willing to directly call my String::Print() member function off of it.  I think you will find that GCC won't allow this with my String Class.  For reasons such as this I am somewhat abandoning GCC.  The Microsoft compiler seems to treat me and my homemade String Class a bit more deferentially! :)

And actually, that's how all the Str$ stuff works too.  Note that those overloaded Str() functions aren't part of my String Class.  They aren't members of it.  What they do is return String Objects through calling various overloaded String Constructors in the manner described above.  As you can see by examining them, there is nothing to the function body at all - just a return statement.  Simplicity itself!  But the return statement causes a call to the appropriate constructor, and that's where the magic, such as it is, occurs.

Yes, I agree with you I need to state more clearly how the code can be used or shared by others.  I had one question about that already over at www.cprogramming.com that I never answered.  Essentially, I created the code for my use, but I'm happy if others can make some use of it.  If others find it of some value I'd simply be grateful to be given some intellectual credit for creating it.  I believe the situation is about the same as with Jose's code, so I'll study his headers. 

Basically, what happened when I finally realized that PowerBASIC couldn't go foreword, I turned to C++ to see if I couldn't squeeze some of the bloatware out of it.  I guess I appreciated the compactness of PowerBASIC code even more than I realized, given the amount of effort I put forth trying to salvage the C++ situation.     

James C. Fuller

#19
Fred,
  I'll take your word for it but José found his first observation of things working in similar circumstances with FreeBasic was due to caching.
Your example may work because of the literals??

How about this:
The local in test allocates memory for string data. How is the local transferred to fs2 in main? The data in fs1 is deallocated when the function terminates and the the fs1 destructor is called.

James



String test (String  fs)
{
    String  fs1 = fs.UCase();
    return fs1;
}


int _tmain (int argc, _TCHAR** argv)
{
    String  fs1 = _T("hello");
    String  fs2;
    fs2 = test( fs1);
    fs2.Print( true);
    getchar();
}


Frederick J. Harris


Both these produced the output ...

HELLO

...for me...


// cl Demo30.cpp Strings.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"

String test (String  fs)
{
    String  fs1 = fs.UCase();
    return fs1;
}


int _tmain()
{
    String  fs1 = _T("hello");
    String  fs2;
    fs2 = test(fs1);
    fs2.Print( true);
    getchar();
   
    return 0;
}




// cl Demo30.cpp Strings.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"

String test(String fs)
{
return fs.UCase();
}

int _tmain()
{
    String  fs1 = _T("hello");
    String  fs2;
    fs2 = test(fs1);
    fs2.Print( true);
    getchar();
   
    return 0;
}


Are you seeing something different?  I only tested with VC15 (VS 2008).   

James C. Fuller

Fred,
  The results are fine I'm just wondering if there is a memory leak or not?
You have a local String which allocates memory for is string data. Now does the caller just get this pointer or is a new one created and the data from the local copied to it?
I know std::string does all the work needed in these situations to prevent a memory leak.

James

Frederick J. Harris

Good question.  I'll check it out in a bit.  Just off the top of my head I'd say C++ is working it all out OK and calling destructors where needed.  My guess is that a constructor is first called to pass the By Value String to the strHello function, and the destructor for that copy is called after the function terminates.  In terms of the local String objects created within the function, their constructors/destructors would be called within that scope.  But I'll test it in a bit, like I said.

James C. Fuller

Fred,
  BCX/bc9 uses a circular static buffer for returning local strings.
This is similar code using bc9 UNICODE "c" strings instead of your String.
James


_TCHAR*   BCX_TmpStr(size_t, size_t = 0, int = 1);

#define cSizeOfDefaultString 2048
#define BCXTmpStrSize  2048

// This is the temporay string allocator
_TCHAR *BCX_TmpStr (size_t Bites, size_t  iPad, int iAlloc)
{
    static int   StrCnt;
    static _TCHAR *StrFunc[BCXTmpStrSize];
    StrCnt = (StrCnt + 1) & (BCXTmpStrSize - 1);
    if(StrFunc[StrCnt]) {
        free (StrFunc[StrCnt]);
        StrFunc[StrCnt] = NULL;
    }
#if defined BCX_MAX_VAR_SIZE
    if(Bites * sizeof(_TCHAR) > BCX_MAX_VAR_SIZE)
    {
        _tprintf(_T("Buffer Overflow caught in BCX_TmpStr - requested space of %d EXCEEDS %d\n"), (int)(Bites * sizeof(_TCHAR)), BCX_MAX_VAR_SIZE);
        abort();
    }
#endif
    if(iAlloc) StrFunc[StrCnt] = (_TCHAR*)calloc(Bites + iPad + 1, sizeof(_TCHAR));
    return StrFunc[StrCnt];
}




_TCHAR * test (const _TCHAR*  s1)
{
    _TCHAR *BCX_RetStr = {0};
    _TCHAR    s2[cSizeOfDefaultString] = {0};
    _tcscpy(s2, ucase(s1));
    BCX_RetStr = BCX_TmpStr(_tcslen(s2), 0, 1);
    _tcscpy(BCX_RetStr, s2);
    return BCX_RetStr;
}


int _tmain (int argc, _TCHAR** argv)
{
    _TCHAR    s1[cSizeOfDefaultString] = {0};
    _TCHAR    s2[cSizeOfDefaultString] = {0};
    _tcscpy(s1, _T("hello"));
    _tcscpy(s2, test(s1));
    _tprintf(_T("%ls\n"), s2);
    Pause();
}

Frederick J. Harris

To test for memory leaks I put debug output code in my String Class methods that are called, particularly the Constructors, operator= calls, and the Destructor.  Also, I had to modify the program in such a way that no String Objects would be created/instantiated in main(), because the Destructor calls on those objects would not occur until main exited, and they might be missed in the console output.  So what I did was change main() to this...


int main()
{
fu();
getchar();
   
return 0;
}


So fu() now has the code which was previously in main()...


void fu()
{
String  fs1 = "hello";
String  fs2;
fs2 = bar(fs1);
fs2.Print( true);
}


And bar() is your previous test(...)...


String bar (String  fs)
{
String  fs1 = fs.UCase();
return fs1;
}


Fair enough?  That shouldn't change anything if memory is being leaked.  I just wanted to make sure all destructor calls were showing up in the console output.  I've had trouble with this in the past.  Perhaps here it may have been unnecessary because I'm running the executable from an open console window.  If you start the console program from a shortcut on your desktop, the destructor calls for any String Objects instantiated in main() would not show up until you pressed the key for getchar() and main terminated, and you could miss them.  But I didn't want to take chances because the point of this exercise is to determine if every String Object being Constructed is being Destroyed.  They all are, as I was quite certain they would be.  Here is the full program....


// cl Demo30.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- TCLib.lib kernel32.lib user32.lib
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "Strings.h"

String bar (String  fs)
{
String  fs1 = fs.UCase();
return fs1;
}

void fu()
{
String  fs1 = "hello";
String  fs2;
fs2 = bar(fs1);
fs2.Print( true);
}

int main()
{
fu();
getchar();
   
return 0;
}


I'll attach Strings.cpp, so you can recreate this if you wish, which I altered to have the console output debug code in it that you'll shortly see the output from.  Here is my annotated console output from a run of the above code...


Console Output:

Entering String::String(const TCHAR* pStr)                     <<< String  fs1 = "hello";     << Construct String From TCHAR*
  this->lpBuffer = 2446283589488
  this->lpBuffer = hello
Leaving String::String(const TCHAR* pStr)

Entering Uninitialized Constructor!                            <<< String  fs2;               << Construct Uninitialized String; This is the 008 String.
  this->lpBuffer = 2446283595008
Leaving Uninitialized Constructor!

Entering String::String(const String& s)                       <<< fs2 = bar(fs1);            << Copy Constructor Makes Copy of fs1 For Function Parameter, and its 040
  this->lpBuffer = 2446283595040
  this->lpBuffer = hello
Leaving String::String(const String& s)

Entering String String::UCase()                                <<< String fs1 = fs.UCase();   << Call String::UCase()
  Entering String::String(const String& s)                     <<< String fs1                 << But 1st need to assign parameter String fs to local String fs1
    this->lpBuffer = 2446283595072                                                            << The 072 String points to "hello"
    this->lpBuffer = hello                                                                    << The 072 String is now "HELLO"
  Leaving String::String(const String& s)
  s.lpBuffer = 2446283595072                                                                                       
  s.lpBuffer = HELLO
Leaving String String::UCase()

Entering String Destructor!                                                                   << Destroy the 040 String constructed for the function parameter
  this->lpBuffer = 2446283595040
Leaving String Destructor!

Entering String& String::operator=(const String& strAnother)   <<< fs2 = bar(fs1);            << The 008 String gets "HELLO" assigned to it;     
  this->lpBuffer = 2446283595008
Leaving String& String::operator=(const String& strAnother)

Entering String Destructor!                                    << Destroy the 072 String Constructed in String::UCase()
  this->lpBuffer = 2446283595072
Leaving String Destructor!

HELLO                                                          <<< fs2.Print( true);          << Output result                                                               

Entering String Destructor!                                    << Destroy fs2 in fu
  this->lpBuffer = 2446283595008
Leaving String Destructor!

Entering String Destructor!                                    << Destroy fs1 from fu
  this->lpBuffer = 2446283589488
Leaving String Destructor!


I have comments to the right of the actual output showing my best guess as to the origin of the debug output.  In summary, there are four String Objects created...

2446283589488
2446283595008
2446283595040
2446283595072

And Four Are Destroyed:

2446283595040
2446283595072
2446283595008
2446283589488

So there are no memory leaks.  I really didn't have to do anything special to achieve this.  The compiler took care of it all through the new[] and delete [] functionality of the programming language.

James C. Fuller

Fred,
  Looks good. I am satisfied. Thank you.

James

Frederick J. Harris

In creating the String Class, surprisingly perhaps, memory leaks were never a problem.   I tested for them many times, like above, but never found any.  Where I've had the most problems was with performance.  As is visible above, there are many 'hidden' calls that take place.  For the way we use Strings for the most part in convenience type operations, the cost is well worth the gain in developer productivity.  The problems only show up in exercises like that John Gleason exercise I've referred to many times where massive strings are involved in repetitious concatenation operations.  Then the overhead imposed by the String Class becomes an issue.  The way I've rationalized it is that for the most part, in my work, I never really do those sorts of things.  I know some folks do but I don't.  If I ever came across a need for performance in my work I'd descend to C isms and the character string primitives like malloc, strcpy, strcat, strncpy, etc.  While it looks like a lot of code, they certainly run fast.  With these you can beat anything in std::string.

James C. Fuller

Fred,
  When I first started my port of José's FreeBasic Afx code to bc9Basic/TCLib I was going to use your String class but I decided to use the default "c" string instead. All the string functions can be called by your String class as they return a _TCHAR*. After your tests here I was again thinking of a String port but with the performance issue you now mention I think I will keep it the way it is. It's the best of both worlds. When I am finished I can package all the Afx code as c++ code as well as bc9Basic.

James