• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

Minimize Program Size By Eliminating The C Runtime Library

Started by Frederick J. Harris, March 23, 2016, 01:17:52 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

Sorry about the stricmp confusion on my part.  Guess I just never used that one.  I'll look into it.  As far as the += (=+?), I had that in my string class for years and years but never actually used it.  When I started working on converting things around in the class in two months ago I removed it.  I'll look it up and post it back in a bit.  I've got literally hundreds of versions of my String Class floating around in about five laptops, and most of them have that in it.

Another thing kind of related to your questions about stricmp is that I don't have a member function to convert a string to upper case.  I think its real easy to code, but I just need to look into it and see if Windows has a function in the kernel to do that which I could use rather than coding it myself.  But if I have to I believe the coding of it just involves adding or subtraction 32 or something like that to the values.  Don't know if that applies to other languages.  Guess it likely does.  I'll get back on this.  My operator== and operator != use case sensitive compares I think.

Frederick J. Harris

Just looked things over and made some important modifications I think and on first analysis at least stuff seems to be working.  Preliminary words though.  What I expect Jim is that you need stricmp to test case insensitive equality between strings.  My String Class doesn't do that and neither does PowerBASIC.  For example, this PB CC6 program will output that the strings aren't equal...


#Compile Exe
#Dim All

Function PBMain() As Long
  Local s1,s2 As WString

  s1="Fred"
  s2="fred"
  If s1 = s2 Then
     Con.Print "They Are Equal!
  Else
     Con.Print "They Aren't Equal!"
  End If
  Con.WaitKey$

  PBMain=0
End Function


What we typically do in BASIC languages to test case insensitive equality is use UCase$() and compare on those terms.  So I need to implement that in my String Class.  Here is my preliminary addition to Strings.h...


String Upper();                                                // Returns a String With this converted to Upper Case.  This Remains Unchanged


Should rename it to UCase() I guess.  Anyway, this seems to work for an implementation...


String String::Upper()
{
String s(*this);
CharUpper(s.lpBuffer);
return s;
}


Might could improve on that --- I'll see.  But to get stuff working the way I wanted, which is this...


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

int main()
{
String s1,s2;

s1=L"Fred";
s2=L"fred";
if(s1.Upper()==s2.Upper())
    printf("They Match!\n");
else
    printf("They Don't Match!\n");
getchar();

return 0;
}


....I had to do something I hate like the dickens to do, but I had to do it.  I had to change another String Class member.  That's quite dangerous to do!  But so far my testing seems to indicate everything's OK, but I need to do more testing.  I had to change my bool operator== member parameter from taking a String& to just a String.  So you would need to go into the Strings.h header and remove the '&' symbol from the parameter and make it like this...


bool operator==(String s);                                     // Compares two Strings for case sensitive equality


and the Strings.cpp file needs to be this...


bool String::operator==(String s)
{
if(_tcscmp(this->lpStr(),s.lpStr())==0)
    return true;
else
    return false;
}


Actually, I think that's better.  It was a long time ago I coded that originally, and I'm not sure why I made the parameter a reference.  It seems to me an ordinary String By Value might be better.  Perhaps there was a reason for it.  If so, if I put this String Class in place of another one in one of my apps there will be problems.  But I never do that.  Anyway, with those changes the above Demo19 should compile and run OK.  And by the way, I can easily add _stricmp or stricmp or whatever it is to TCLib.  Funny how I missed that one anyway.  Likely because I was following the BASIC model of case sensitive equality tests, which use strcmp.  Let me know what you think of what I've done.  And I'll post += code in a bit.

James C. Fuller

Fred,
  I ran into this code somewhere?
James


#define TOUPPER(CH) \
  (((CH) >= 'a' && (CH) <= 'z') ? ((CH) - 'a' + 'A') : (CH))

int
stricmp (const char *s1, const char *s2)
{
  while (*s2 != 0 && TOUPPER (*s1) == TOUPPER (*s2))
    s1++, s2++;
  return (int) (TOUPPER (*s1) - TOUPPER (*s2));
}


James C. Fuller

Fred,
  Add this to Demo11.cpp

    _TCHAR     A[2048];
    _tcscpy(A, _T("This is a test string"));

just before
    s1=_T("Zero,  One, Two  , Three ,  Four, Five   , Six, Seven, Eight , Nine, Ten");

I get: unresolved external symbol __chkstk referenced in function wmain

James

Frederick J. Harris

#19

_TCHAR     A[2048];


Yep, can't do that.  Only about 1k is being allocated for the stack.  Anything over that causes the compiler to emit those stack probes in the code.  I believe it can be fixed though.  I haven't ran into that yep, but Martins Mozeiko addressed that issue in his write up which I referenced.  I believe its a linker setting where you specify a bigger stack.

Frederick J. Harris

Here's the link again...

https://hero.handmadedev.org/forum/code-discussion/79-guide-how-to-avoid-c-c-runtime-on-windows

Maybe with that little glitch it might be beginning to dawn on you what I've been going through with this.


Frederick J. Harris

Here is Strings.h with the += stuff added.  I put a #define PLUS_EQUAL in the header and Strings.cpp because that isn't something I ever use.  I just don't personally care for that notation.  Remove that from your version if you like.  There is one other C ism I don't ever use and its that stuff in parentheses with : and ? symbols.  I particularly hate that.  Never used it, and I inwardly groan when I see it.  Here is the new Strings.h


// Strings.h                                                    // NULL Terminated String Class With Method Naming Conventions After BASIC Model Languages.  Implemented For
#ifndef Strings_h                                               // x86 / x64, Asci / Unicode (For x86 Builds Comment Out x64 #define Below Left).
#define Strings_h

#ifndef ssize_t
typedef SSIZE_T ssize_t;                                        // ssize_t is defined in GCC, but isn't defined in VC 9-15, but rather SSIZE_T.  For symetry we'll define it.
#endif

#define MINIMUM_ALLOCATION                   16                 // allocate at least this for every String
#define EXPANSION_FACTOR                      2                 // repeated concatenations will keep doubling buffer
#define INTEGRAL_CONVERSIONS                                    // allows direct assignment of integral numeric values to Strings, e.g., String s(12345) or s=54321;
#define FLOATING_POINT_CONVERSIONS                              // allows direct assignment of floating point values to Strings, e.g., String s(3.14159) or s=2.98;
#define FORMATTING                                              // put commas every three places, rounding, left/right justification, specify field sizes (padding), etc.
#define PLUS_EQUAL                                              // Enables += Concatenation of TCHARs, TCHAR* and TCHAR&
#define CONSOLE_OUTPUT                                          // support console output, i.e., enable String::Print()
//#define x64

class String
{
public:                                                        // Constructors (10)
String();                                                      // Uninitialized Constructor
String(const int iSize, bool blnNullOut);                      // Constructor creates String of size iSize and optionally nulls out
String(const TCHAR ch);                                        // Constructor creates a String initialized with a char, e.g., String c('A');
String(const TCHAR* pStr);                                     // Constructor: Initializes with char*, e.g. s1 = "PowerBASIC! Compile Without Compromise!"
String(const String& strAnother);                              // Constructor creates String initialized with another already existing String, e.g., String s2(s1);
#ifdef INTEGRAL_CONVERSIONS
    String(int iNumber);                                        // Constructor creates String initialized with an int, e.g., String s1(2468); kind of Str$(2468) in PowerBASIC
    String(unsigned int uNumber);                               // Constructor creates String initialized with an unsigned int, e.g., String s1(2468); kind of Str$(2468) in PowerBASIC
    #ifdef x64
       String(size_t  uiNumber);                                // Constructor creates String from 64 bit unsigned number, e.g., String strBigNum(12345678987654);
       String(ssize_t iNumber);                                 // Constructor creates String from 64 bit signed number, e.g., String strBigNum(-12345678987654);
    #endif
#endif
#ifdef FLOATING_POINT_CONVERSIONS
    String(double dblNumber);                                   // Constructor creates String from floating point number, e.g., String s(3.14159);
#endif
String& operator=(const TCHAR c);                              // Assign a char to a String, e.g., String s='A';
String& operator=(const TCHAR* pStr);                          // Assign a character string to a String Object, e.g.,  String s1 = "Compile Without Compromise";
String& operator=(const String& strAnother);                   // Assign an already existing String to this one, e.g., String s2 = s1;
#ifdef INTEGRAL_CONVERSIONS
    String& operator=(int iNumber);                             // Assign an int converted to a String to this, e.g., String s1 = -123456789;
    String& operator=(unsigned int uNumber);                    // Assign an unsigned int converted to a String to this, e.g., String s1 =  123456789;
    #ifdef x64
       String& operator=(size_t  uNumber);                      // Assign a 64 bit unsigned quantity converted to a String to this, e.g., String s2 =  12345678987654;
       String& operator=(ssize_t iNumber);                      // Assign a 64 bit   signed quantity converted to a String to this, e.g., String s2 = -12345678987654;
    #endif
#endif
#ifdef FLOATING_POINT_CONVERSIONS
    String& operator=(double dblNumber);                        // Assign a double converted to a String to this, e.g., String strDouble = 3.14159;
#endif
String operator+(const TCHAR ch);                              // Concatenates or adds a character to an already existing String, e.g., s1 = s1 + 'A';
String operator+(const TCHAR* pChar);                          // Concatenates or adds a character array (char*) to an already existing String, e.g., s1 = s1 + lpText;
String operator+(String& s);                                   // Concatenates or adds another String to this one, e.g., s1 = s1 + s2;
#ifdef PLUS_EQUAL
    String& operator+=(const TCHAR ch);                         // Add TCHAR to this
    String& operator+=(const String&);                          // Adds a String to this and assigns it to left of equal sign
    String& operator+=(const TCHAR*);                           // Adds a TCHAR* to this and assigns it to left of equal sign
#endif
bool operator==(String s);                                     // Compares two Strings for case sensitive equality
bool operator==(const TCHAR* pChar);                           // Compares a String against a char* for case sensitive equality
bool operator!=(TCHAR* pChar);                                 // Compares a String against a char* for case sensitive inequality
void LTrim();                                                  // Removes leading white space by modifying existing String
void RTrim();                                                  // Removes trailing white space by modifying existing String
void Trim();                                                   // Removes leading or trailing white space from existing String
String UCase();                                                // Returns a String With this converted to Upper Case.  This Remains Unchanged
String Left(size_t iCntChars);                                 // Returns a String consisting of left-most iCntChars of this
String Right(size_t iCntChars);                                // Returns a String consisting of right-most iCntChars of this
String Mid(size_t iStart, size_t iCount);                      // Returns a String consisting of iCount characters of this starting at one based iStart
int ParseCount(const TCHAR delimiter);                         // Returns count of delimited fields as specified by char delimiter, i.e., comma, space, tab, etc.
void Parse(String* pStr, TCHAR delimiter, size_t iParseCount); // Parses this based on delimiter.  Must pass in 1st parameter String* to sufficient number of Strings
String Remove(const TCHAR* pCharsToRemove);                    // Returns A String With All The chars In A char* Removed (Individual char removal)
String Remove(const TCHAR* pStrToRemove, bool bCaseSensitive); // Returns a String with 1st parameter removed.  2nd is bool for case sensitivity.
String Replace(TCHAR* pToRevove, TCHAR* pReplacement);         // Replaces pToRemove with pReplacement in new String.  Replacement can cause String to grow

int InStr                                                      // Returns one based position of pStr in this by case sensitivity and left/right starting position
(
  const TCHAR* pStr,                                            // pStr -- TCHAR* To Character String To Search For
  bool  blnCaseSensitive,                                       // true / case sensitive; false / case insensitive
  bool  blnStartLeft                                            // Can Specify Search Start From Beginning Or End
);

int InStr                                                      // Returns one based position of String in this by case sensitivity and left/right starting position
(
  const String& str,                                            // String To Search For
  bool  blnCaseSensitive,                                       // true / case sensitive; false / case insensitive
  bool  blnStartLeft                                            // Can Specify Search Start From Beginning Or End
);
 
#ifdef FORMATTING
    void Format                                                 // dblNumber converted to String, in iArrayCount Buffer Size, with iDecPlaces right of decimal, left or right justified.
    (
     double dblNumber,
     size_t iArraySizeCountOfObjects,                           // Number Of Elements In TCHAR Array.  If You Have A 64 Byte wchar_t buffer, then this number will be 32 for 32 elements.
     size_t iDecimalPlaces,                                     // It includes the NULL.  So its the size of the underlying memory allocation.  Number of decimal places to right of decimal
     TCHAR  cDecimalSeperator,                                  // In US We Use '.'.  In Europe mostly this -- ','
     bool   blnRightJustified                                   // Left/Right Justify Result in iArraySizeCountOfObjects Buffer.
    );
   
    void Format                                                 // double converted to Str, in iArCnt Buf Size, with iDecPlaces right of dec, with cSeperator for thousands, l/r justified.
    (
     double dblNumber,                                          // double to be converted to String
     size_t iArraySizeCountOfObjects,                           // See above.  Size of buffer in elements.  If the buffer is this ... TCHAR szBuf[32], then this parameter is 32.
     size_t iDecimalPlaces,                                     // Number of decimal places to right of decimal
     TCHAR  cThousandsSeperator,                                // Period, comma, whatever
     TCHAR  cDecimalSeperator,                                  // Period, comma, whatever
     bool   blnRightJustified                                   // true is right justified; false left justified in iArraySizeCountOfObjects buffer
    );
   
    void Format                                                 // For integral numbers; can specify justification, field width, and seperator for thousands place
    (
     ssize_t iNumber,                                           // Integral number to format
     size_t  iArraySizeCountOfObjects,                          // See above.  For TCHAR* pBuffer=(TCHAR*)malloc(32*sizeof(TCHAR)), it will be 32
     TCHAR   cThousandsSeperator,                               // Comma, period, whatever
     bool    blnRightJustified                                  // true is right justified; false left justified in iArraySizeCountOfObjects buffer
    );                   
#endif

#ifdef CONSOLE_OUTPUT
    void Print(bool blnCrLf);                                   // Outputs String with or without CrLf
    void Print(const TCHAR* pText, bool blnCrLf);               // Parameter #1 - leading text literal const; Parameter #2 - with/without CrLf
#endif
size_t Len();                                                  // accessor for String::iLen member
size_t Capacity();                                             // Will be one less than underlying memory allocation
TCHAR* lpStr();                                                // Same as std::string.c_str().  Returns pointer to underlying Z String
~String();                                                     // String Destructor

private:
TCHAR*    lpBuffer;                                            // Buffer controlled by String
size_t    iLen;                                                // Keeps track of present length of String
size_t    iCapacity;                                           // Keeps track of present capacity of String.  Will be one less element than length of this->lpBuffer memory allocation
};

String operator+(char* lhs, String& rhs);
#endif
// End Strings.h


I don't want to waste storage by posting the whole of Strings.cpp, so here are just those three added functions...


#ifdef PLUS_EQUAL
   String& String::operator+=(const TCHAR ch)
   {
    size_t iTot=this->iLen+1;
    if(iTot>this->iCapacity)
    {
       int iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
       TCHAR* pNew=new TCHAR[iNewSize];
       _tcscpy(pNew,this->lpBuffer);
       delete [] this->lpBuffer;
       this->lpBuffer=pNew;
       this->lpBuffer[iTot-1]=ch;
       this->lpBuffer[iTot]=_T('\0');
       this->iCapacity=iNewSize-1;
       this->iLen=iTot;
    }
    else
    {
       this->lpBuffer[iTot-1]=ch;
       this->lpBuffer[iTot]=_T('\0');
       this->iLen=iTot;
    }

    return *this;
   }


   String& String::operator+=(const TCHAR* pStr)
   {
    size_t iStrlen=_tcslen(pStr);
    size_t iTot=iStrlen+this->iLen;
    if(iTot>this->iCapacity)
    {
       int iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
       TCHAR* pNew=new TCHAR[iNewSize];
       _tcscpy(pNew,this->lpBuffer);
       delete [] this->lpBuffer;
       this->lpBuffer=pNew;
       _tcscat(pNew,pStr);
       this->iCapacity=iNewSize-1;
       this->iLen=iTot;
    }
    else
    {
       _tcscat(this->lpBuffer,pStr);
       this->iLen=iTot;
    }

    return *this;
   }


   String& String::operator+=(const String& strRef)
   {
    size_t iTot=strRef.iLen+this->iLen;
    if(iTot>this->iCapacity)
    {
       int iNewSize=(iTot*EXPANSION_FACTOR/16+1)*16;
       TCHAR* pNew=new TCHAR[iNewSize];
       _tcscpy(pNew,this->lpBuffer);
       delete [] this->lpBuffer;
       this->lpBuffer=pNew;
       _tcscat(pNew,strRef.lpBuffer);
       this->iCapacity=iNewSize-1;
       this->iLen=iTot;
    }
    else
    {
       _tcscat(this->lpBuffer,strRef.lpBuffer);
       this->iLen=iTot;
    }

    return *this;
   }
#endif


Seems to work.  Here is Demo20.cpp...


// cl Demo20.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"
extern "C" int _fltused=1;

int main()
{
const wchar_t* szNames[]={L"frank ", L"Samuel ", L"Edward ", L"Martin ", L"Luther ", L"Frederick"};
String s1;

for(size_t i=0; i<6; i++)
     s1+=szNames[i];
s1.Print(true);
getchar();

return 0;
}


Let me know how that stack modfication code goes.  I was going to look into that.  All the stuff I've done so far are pretty small programs with not too much stack space used.  If Martens says that'll fix it, you can take that to the bank.  He's smart.

Frederick J. Harris

Oh!  Don't want to cause you too much grief!  In that Strings.h file I have the #define x64 commented out.  Just so happens I was coding today on my old HP Pavilion laptop circa 2006 or so.  And it has a 32 bit OS (64 bit AMD chip though).  So you'll likely get some grief if you don't comment that back in.  Also, I may have changed that String::Upper() to String::UCase().  So be wary of that.  Otherwise good (I hope).

I have three laptops I code on.  I don't use desktops at all because of a neck problem I have.  Was backended at a red light 25 years ago and it totalled my neck.  Have had two fusions on it but its still extremely bad.  I can only take so much sitting and I have to get up and work standing.  Also with laptops I can more easily position the machine so as not to cause pain.

Can't believe that old Pavilion is still working.  Will be ten years old in June.  It was a $2500 machine though.  Actually, it may be the best working box I have.  That's the one I take with me every year on my 4 - 5 week long explorations of the mountains and deserts out West.  It bounces around in the back of my 4X4 but always works. 

My Dell M6500 Win 7 is my main workhorse though.  Very unstable, however.  Crashes about every other day.  Bad graphics driver I expect.  And I bought the HP Envy about 6-7 weeks ago specifically for this LibCTiny project to test on VC19.  Seems OK.  Time will tell.  Main reason I bought that was because I figured I owed HP for my old Pavilion being such a wonderful machine.   

Frederick J. Harris


#define TOUPPER(CH) \
  (((CH) >= 'a' && (CH) <= 'z') ? ((CH) - 'a' + 'A') : (CH))

int
stricmp (const char *s1, const char *s2)
{
  while (*s2 != 0 && TOUPPER (*s1) == TOUPPER (*s2))
    s1++, s2++;
  return (int) (TOUPPER (*s1) - TOUPPER (*s2));
}


Yea, that looks good.  Another advantage of that code is that it would work on *nix or whatever, whereas my String::UCase() wouldn't because CharUpper() is of course a Windows kernel32 function.  But for this particular project we're really pretty much focusing on Windows.  Not sure how all this would play out with Linux.  Possibly not needed there cuz last time I worked with Linux, binaries weren't out of control like with Windows.  That was a long time ago, however.

James C. Fuller

Fred,
  Changes look good.
Is there another way to do this?
I need two numerics added to an fstring.
  INT_PTR  I = (INT_PTR)hDlg2;
  fstring  fs1(_T("Open test.avi type AVIVideo alias AVIFile parent "));
  fstring  fs2(I);
  fstring  fs3(WS_CHILD);
  fs1 +=   fs2;
  fs1 +=  _T(" STYLE ");
  fs1 +=   fs3;

James

James C. Fuller

Fred,
  I thought the added STACK fixed it but it did not in this case.
James



#define UNICODE
#define _UNICODE


#include <windows.h>
#include "TCLib\stdlib.h"
#include "TCLib\stdio.h"
#include "TCLib\tchar.h"
#include "TCLib\Strings.cpp"
typedef String fstring;
extern "C" int _fltused = 1;

#define cSizeOfDefaultString 2048

int     _tmain (void);

int _tmain ()
{
    _TCHAR    s1[cSizeOfDefaultString];
    _TCHAR    s2[cSizeOfDefaultString];
    fstring  fs1(_T("Hello, World!"));
    int      i = {0};
    _tcscpy(s1, _T("HELLo"));
    _tcscpy(s2, _T("hello"));
    getchar();
    return 0;
}




Batch file I used to compile:


@setlocal enableextensions enabledelayedexpansion
@ECHO OFF

SET F=%~nx1
IF EXIST "%F%.cpp" (
  SET FN="%F%.cpp"
  GOTO start
)
GOTO usage

:start
SET XTYPE=x86_amd64
CALL "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %XTYPE%


REM cl %FN%  /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib user32.lib %2 %3
cl %FN%  /O1 /Os /GS- /Zc:sizedDealloc- /link /STACK:0x100000,0x100000 TCLib.lib kernel32.lib user32.lib %2 %3
:cleanup
ECHO Finished!
IF EXIST "%F%.obj" del "%F%.obj"
IF EXIST "Strings.obj" del "Strings.obj"

GOTO done

:usage
ECHO **************************************************************
ECHO  Usage:  FRED.BAT MainFile  no ext .cpp assumed
ECHO **************************************************************
:done

Frederick J. Harris

Just tried this from my Code::Blocks environment (not TCLib - but I expect it should work the same)...


#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <Windows.h>
#include <tchar.h>
#include <cstdio>
#include "Strings.h"

int main()
{
HWND hDlg=(HWND)123456789;
String s1=(_T("Open test.avi type AVIVideo alias AVIFile parent "));
String s2((size_t)hDlg);
String s3(WS_CHILD);
s1=s1+s2+_T(" STYLE ")+s3;
s1.Print(true);
getchar();

return 0;
}

// Open test.avi type AVIVideo alias AVIFile parent 123456789 STYLE 1073741824

Frederick J. Harris

Was hoping the stack entry from Mozeiko's post would take care of the issue.  But this might be something else.  When you use my String Constructor like that its making a dynamic memory allocation with new.  So its not a stack size allocation issue  don't think.  Might be different issue.  I'll have to look into it.  Might take some time.

James C. Fuller

Fred,
  Sorry, MY BAD!
I forgot to add /Gs9999999.
Preliminary tests now seem ok.

James

Frederick J. Harris

Whew!  You really don't have any idea how glad I am to hear that James.  In a way you are getting ahead of me.  I guess really I'm still at the proof of concept stage.  Let me tell you what immediately went through my mind when you said that constructor was failing.  First, I have the C++ new operator implemented with HeapAlloc().  If that was failing it means that somehow or other the situation wasn't too different from the stack, i.e., only some miniscule heap was being set up due to the elimination of the C Runtime.  To the best of my knowledge the only way to fix that would be through the VirtualAlloc() functions and creating one's own heap().  I'm aware of the issue but have never worked with it.  And I'd prefer not having to if I can avoid it. ;D  I like to try to win wars by fighting as few battles as possible!

By the way James, how would I use your batch files?  I just paste my command line compilation string into my console window.  Your way is better, easier???  I think you are likely much more advanced on batch files than I.