IT-Consultant: Frederick J. Harris > Discussion

Minimize Program Size By Eliminating The C Runtime Library

(1/7) > >>

Frederick J. Harris:
Using TCLib.lib And String Class For x86/x64/ascii/unicode Application Development
     TCLib is a replacement for the Microsoft C Runtime Library whose purpose is to allow the creation of applications consisting of very small executable files.  Console mode or GUI windows can be created with executable sizes starting in the 2 or 3 kilobyte range.  This feat is achieved by not linking against the C Runtime Library, statically or dynamically.  This can be done because the modular design of the C and C++ languages separate core language features from input/output activities and Runtime Library support.   

     My String Class consisting of two files named Strings.h and Strings.cpp is a replacement for the C++ Standard Library String Class, but it also contains some of the functionality of the C++ iostream Class.  It is desirable to use in conjunction with the code I’ll provide to eliminate the C Runtime because the manipulation of C style character arrays is a very awkward way of dealing with strings in the context of high level application development work.  My String Class works with TCLib.lib but the C++ Standard Library String Class, iostreams, and in fact everything in the C++ Standard Library won’t.  It’ll be off limits to you so I want to point that out up front.  If achieving small non-bloated programs is important to you, the above C++ Libraries must be avoided at all costs, as it is the design and construction of those libraries which causes inflated binaries which we term as ‘bloatware’.   

     First off, if you don’t have TCLib.lib and want to create it yourself from scratch, or you just want to know how to do it, you’ll need these *.cpp and *.h files which I’ve provided in the download and will also post in their entirety later…


--- Code: ---crt_con_a.cpp       Process entry point for asci console programs     
crt_con_w.cpp       Process entry point for wide console programs
crt_win_a.cpp       Process entry point for asci GUI programs
crt_win_w.cpp       Process entry point for wide GUI programs
memset.cpp          Replacement for C Runtime memset()
memcpy.cpp          Replacement for C Runtime memcpy()
newdel.cpp          Implementation of C++ new and delete operators
printf.cpp          Replacement for C Runtime printf() – Thanks Matt Pietrek!
sprintf.cpp         Replacement for C Runtime srintf() – Thanks Matt Pietrek!
_strnicmp.cpp       My Replacement for C Runtime _strnicmp
strncpy.cpp         My Replacement for C Runtime strncpy
strncmp.cpp         My Replacement for C Runtime strncmp
_strrev.cpp         My Replacement for C Runtime _strrev
strcat.cpp          My Replacement for C Runtime strcat
strcmp.cpp          My Replacement for C Runtime strcmp
strcpy.cpp          My Replacement for C Runtime strcpy
strlen.cpp          My Replacement for C Runtime strlen
getchar.cpp         My Replacement for C Runtime getchar
alloc.cpp           Replacement for C Runtime memory allocation functions from Matt Pietrek
alloc2.cpp          Replacement for C Runtime memory allocation functions from Matt Pietrek
allocsup.cpp        Replacement for C Runtime memory allocation functions from Matt Pietrek
FltToCh.cpp         My replacement for floating point support only available in C Runtime
atol.cpp            My Replacement for C Runtime atol
_atoi64.cpp         My Replacement for C Runtime _atoi64
abs.cpp             My Replacement for C Runtime abs
win32_crt_math.cpp  (only used in x86) Big Thanks To Martins Mozeiko From Handmade Hero For This!

malloc.h
memory.h
stdio.h
stdlib.h
string.h
tchar.h

--- End code ---

Running TCLib.mak (just below) with Microsoft’s nmake.exe utility will create the library.  Note towards the bottom is this line, which are the directions to lib.exe to create the lib…


--- Code: ---LIB /NODEFAULTLIB /machine:x64 /OUT:$(PROJ).LIB $(OBJS)

--- End code ---

For creating a 32 bit version of TCLib.lib, the /machine: specification should be x86 – not x64.  I suppose I haven’t pointed out that this code works for both x86 and x64, but if you want both which you possibly will you need to create separate directories for both, and you need to create and use two separate libs.  This file is for x64, as you can see by my machine specification of x64 as described above….

// TCLib.mak

--- Code: ---PROJ       = TCLib

OBJS       = crt_con_a.obj crt_con_w.obj crt_win_a.obj crt_win_w.obj memset.obj newdel.obj printf.obj \
             sprintf.obj _strnicmp.obj strncpy.obj strncmp.obj _strrev.obj strcat.obj strcmp.obj \
             strcpy.obj strlen.obj getchar.obj alloc.obj alloc2.obj allocsup.obj FltToCh.obj atol.obj \
             atoi64.obj abs.obj memcpy.obj win32_crt_math.obj
       
CC         = CL
CC_OPTIONS = /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN

$(PROJ).LIB: $(OBJS)
    LIB /NODEFAULTLIB /machine:x64 /OUT:$(PROJ).LIB $(OBJS)

.CPP.OBJ:
    $(CC) $(CC_OPTIONS) $<

--- End code ---

If you aren’t familiar with doing these sorts of things, simply create a directory/folder somewhere, open a command prompt to that directory, put all of the above files (the *.cpp and *.h files) in that directory including TCLib.mak, and execute nmake on TCLib.mak as follows…

C:\SomeDirectory>nmake TCLib.mak [ENTER]

Note that the path to the Microsoft Compiler Toolchain must be properly set for the operating system to find nmake from any arbitrary folder on your computer.  For Microsoft products (which is all this code and work applies to, really), when you download the SDK or install Visual Studio, Microsoft puts shortcuts on your Start Menu to batch files which when executed set the paths correctly to the compiler toolchain.  On my computer with Visual Studio 2015 this is the target and command line switch of the shortcut I’m using for x64 compiles….

""C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"" amd64

The ‘amd64’ command line argument to vcvarsall.bat tells this batch file to set the compiler to run in x64 mode.

     Having successfully created TCLib.lib as above, or just using the supplied one, lets start with a simple Dennis Ritchie Hello, World! Program.  Here is Demo1.cpp…


--- Code: ---// cl Demo1.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 2,048 bytes x86 ASCII, 2,048 bytes x86 UNICODE VC15
// 3,072 bytes x64 ASCII, 3,072 bytes x64 UNICODE VC19
#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include "stdio.h"
#include "tchar.h"

int _tmain()
{
 _tprintf(_T("Hello, World!\n"));
 getchar();

 return 0;
}

// Output:
// =============
// Hello, World!

--- End code ---

     That compiles to various numbers between 2k and 3k depending on which version of the Microsoft compiler you are using, whether you are doing an ascii or unicode build, or whether you are doing x86 or x64.  It compares very favorably with even asm code as below which compiles to just 2.5 k…


--- Code: ---; C:\Code\MASM\Projects\Demo1>ml /c /coff /Cp hello2.asm
; C:\Code\MASM\Projects\Demo1>link /SUBSYSTEM:CONSOLE hello2.obj
; 2,560 Bytes
.386
.model flat,stdcall
option casemap:none

include     \masm32\include\windows.inc
include     \masm32\include\kernel32.inc
include     \masm32\include\msvcrt.inc

includelib  \masm32\lib\kernel32.lib
includelib  \masm32\lib\msvcrt.lib

.data
msg         db 'Hello, World!',13,10,0

.code
start:
  invoke  crt_printf, ADDR msg
  invoke  crt_getchar
  invoke  ExitProcess, 0
END start

--- End code ---
 

…although to be fair, I have to admit I am using the C Runtime printf for console output above in the asm code.  Getting back to Demo1.cpp, bear with me while I discuss that program in some detail.  This line…


--- Code: ---cl Demo1.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib   

--- End code ---

…invokes the Microsoft compiler cl.exe.  The ‘/O1’ and ‘Os’ switches tell the compiler to optimize for small code size and prevent debugging symbols from being put in the binary output.  The ‘/GS-‘ switch turns off security checks.  This must be done as some of the code for implementing that is in the C Standard Library which isn’t present to be linked against.  If it isn’t specified the code won’t link.

     The ‘/link TCLib.lib kernel32.lib’ tells the linker which libraries to look in first when resolving function or variable references found in the code.  It must not find in the code any function or variable references not found in either of those libraries, or it will load the C Standard Library and start looking there to resolve them.  When it does that it will encounter naming collisions with the substitute functions I have provided in TCLib.lib, and it will not know which code to use.  At that time linker errors will be generated and the executable won’t be produced and you’ll be looking at bizarre linker errors the likes of which you’ve never seen before and you’ll be SOL and very unhappy.  That’s why you can’t use anything from the C++ Standard Library such as its string class - std::string or std::wstring. 

     Note we need to include windows.h in all these programs because a substantial amount of the support which would ordinarily come from the C Runtime has to come from somewhere else if we eliminate it, and that somewhere else is in many cases the Windows libraries, which require us to include windows.h.  These lines…


--- Code: ---#define UNICODE
#define _UNICODE

--- End code ---

…tell the preprocessor we’re doing a wide character build.  That will greatly affect how these files are processed…


--- Code: ---#include <windows.h>
#include "stdio.h"
#include "tchar.h"

--- End code ---

     Note the top file uses ‘<>’ symbols to enclose a file name, and the bottom two file names are encased with quotes.  A small detail, seemingly, but this is important!  The brackets tell the preprocessor that the indicated file will be found in the system PATH as specified in the environment for include files.  Alternately, the enclosing quotes tell the preprocessor that the file is an application specific file found in the same directory with the application’s source code files.  Stdio.h and tchar.h are actually system include files, but I had to make up TCLib specific renditions of these files, which is unusual I admit, because various things encountered within those system files of the same name would cause the compiler or linker to throw all kinds of bizarre warnings and errors that will ruin your life trying to track down and fix.  Believe me, I know.  I have supplied my versions of those files for you, and you’ll see they are much simpler and smaller than the system files of the same name.  Bottom line – be very careful about how you include files in terms of ‘< >’ symbols or double quotes.

     Moving on, this symbol…


--- Code: ---int _tmain()

--- End code ---

…will resolve through the mysterious alchemy of tchar.h to…


--- Code: ---wmain

--- End code ---

…if UNICODE is defined (which it is), or to…


--- Code: ---main

--- End code ---

…if it isn’t.  Lets take a look at what the above program compiles to size wise if we use the C Runtime.  That will give us some idea of what we’re saving by eliminating it.  Here would be that…


--- Code: ---// cl Hello.cpp /O1 /Os /MT    // Optimize for small code and stand alone build with no redistributables needed.
// 119,296 bytes x64 ASCII     // Things have gotten so bad we need 120 k for Hello, World!!!!!!
#include <stdio.h>             // We can and should use ‘< >’ symbols on stdio.h because we’re using the system file.

int main()
{
 printf("Hello, World!\n");
 getchar();

 return 0;
}

// Output:
// =============
// Hello, World!

--- End code ---

     So we’re saving something like one hundred and sixteen to one hundred and seventeen thousand bytes if using Microsifts VC19 compiler.  The situation is the same with GUIs.  Here’s Form1.cpp 


--- Code: ---// cl Form1.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib
#define UNICODE        //  3,072 Bytes x64 UNICODE or ASCI With LibCTiny.lib
#define _UNICODE       // 84,992 Bytes With C Standard Library Loaded (LIBCMT.LIB)
#include <windows.h>
#include "tchar.h"

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 if(msg==WM_DESTROY)
 {
    PostQuitMessage(0);
    return 0;
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
 WNDCLASSEX wc={0};
 MSG messages;
 HWND hWnd;

 wc.lpszClassName = _T("Form1");
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.hInstance     = hInstance;
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

--- End code ---

We’re at only 3 k with that in x64 wide character.  I believe with the older VC15 from Visual Studio 2008 running in x86 its 2,560 bytes.  The situation with pure asm is similar.  Here is the above in 32 bit masm, which also compiles to 2,560 bytes…


--- Code: ---.386
.model     flat, stdcall
option     casemap:none
include    \masm32\include\windows.inc
include    \masm32\include\user32.inc
include    \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain proto hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD

.Data
ClassName   db "Form2", 0
AppName     db "Form2", 0

.Data?
hInstance   HINSTANCE ?
CommandLine LPSTR     ?

.Code
start:

invoke GetModuleHandle, NULL
mov    hInstance, eax
invoke GetCommandLine
mov    CommandLine, eax
invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
invoke ExitProcess, eax


WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  .IF uMsg==WM_DESTROY
      invoke PostQuitMessage, NULL
  .ELSE
      invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
  .ENDIF
  xor eax, eax
  ret
WndProc endp


WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
  LOCAL wc  : WNDCLASSEX
  LOCAL msg : MSG
  LOCAL hwnd: HWND
 
  mov    wc.cbSize, SIZEOF WNDCLASSEX
  mov    wc.style, CS_HREDRAW or CS_VREDRAW
  mov    wc.lpfnWndProc, OFFSET WndProc
  mov    wc.cbClsExtra, NULL
  mov    wc.cbWndExtra, NULL
  push   hInstance
  pop    wc.hInstance
  mov    wc.hbrBackground, COLOR_WINDOW + 1
  mov    wc.lpszMenuName, NULL
  mov    wc.lpszClassName, OFFSET ClassName
  invoke LoadIcon,NULL, IDI_APPLICATION
  mov    wc.hIcon, eax
  mov    wc.hIconSm, eax
  invoke LoadCursor, NULL, IDC_ARROW
  mov    wc.hCursor, eax
  invoke RegisterClassEx, addr wc
  INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,100,100,375,350,NULL,NULL,hInst,NULL
  mov    hwnd, eax
  invoke ShowWindow, hwnd, SW_SHOWNORMAL
  invoke UpdateWindow, hwnd
  .WHILE TRUE
      invoke GetMessage, ADDR msg, NULL, 0, 0
      .BREAK .IF (!eax)
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage, ADDR msg
  .ENDW
  mov    eax, msg.wParam
  ret
WinMain endp
end start

--- End code ---

     So the inevitable conclusion at which one must arrive is that it isn’t the C++ language which is bloated or produces bloatware; it’s the libraries you use which produces that effect.  Lets move on to Demo2.cpp which actually does something a little hard because we’re using floating point math.  That has been the most difficult part by far of developing this library, because all the support for manipulating floating point numbers is ultimately in the C Standard Library, which we’ve eliminated.

continued...

Frederick J. Harris:

--- Code: ---// cl Demo2.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 4,096 bytes x86 ASCII, 4,096 bytes x86 UNICODE  VC15
// 5,120 bytes x64 ASCII, 5,632 bytes x64 UNICODE  VC19
#define UNICODE
#define _UNICODE
#include  <windows.h>
#include  "stdio.h"
#include  "tchar.h"
#define   x64
extern "C" int _fltused=1;

int _tmain()
{
 double dblNums[]={123456.78912, -456.9876, 9999.99999, -0.987654, 0.0, 1.985};
 TCHAR szBuffer[16];
 int iLenRet=0;

 _tprintf(_T("i       iLenRet   dblNums[i]\n"));
 _tprintf(_T("============================\n"));
 for(size_t i=0; i<6; i++)
 {
     iLenRet=FltToTch(szBuffer, dblNums[i], 16, 2, _T('.'),true);
     #ifdef x64
        _tprintf(_T("%-4Iu%8d%16s\n"),i,iLenRet,szBuffer);
     #else
        _tprintf(_T("%-4u%8d%16s\n"),i,iLenRet,szBuffer);
     #endif
 }
 getchar();

 return 0;
}


#if 0

i       iLenRet   dblNums[i]
============================
0         15       123456.79
1         15         -456.99
2         15        10000.00
3         15           -0.99
4         15            0.00
5         15            1.98

#endif

--- End code ---

     Almost all of the past two months working on this project has been involved in one way or another with solving the floating point math problem.  As you can see above I have an array of six doubles of rather widely differing sizes.  I try to print them out to the console with right justified formatting and rounded to two decimal places.  I had to code that all myself because Windows relies on functionality in the C Standard Library to deal with floating point math.  Very surprisingly, the problem in doing this wasn’t x64.  All the problem was in x86 32 bit.  You’ll see a function above which I named FltToTch.  That’s actually a macro defined in my version of tchar.h for FltToCh and FltToWch.  Here are the function declarations.  Depending on whether UNICODE is defined or not, FltToTch resolves to either…


--- Code: ---size_t __cdecl FltToCh(char* p, double x, size_t iFldWthTChrs, int iDecPlaces, char cDecimalSeperator, bool blnRightJustified);

--- End code ---

…or this…


--- Code: ---size_t __cdecl FltToWch(wchar_t* p, double x, size_t iFldWthTChrs, int iDecPlaces, wchar_t cDecimalSeperator, bool blnRightJustified);

--- End code ---

     You’ll only need to use this function if you want to output floating point numbers.  For integral numbers, i.e., ints, longs, _int64s, or strings, or anything else, you can just use printf or sprintf and their tchar varieties.  They work as expected.  If doubles need to be output through use of the above function, you’ll need to provide a buffer where the output of the conversion is to be written.  In that sense, the function is a bit like sprintf.  The first parameter is that buffer…


--- Code: ---size_t __cdecl FltToCh
(
  char* p,                   // Pointer to buffer of a size large enough to accommodate converted double
  double x,                  // The double to be converted
  size_t iFldWthTChrs,       // Should be the total size of 1st parameter buffer in TCHARs including NULL
  int iDecPlaces,            // Number of decimal places in desired output
  char cDecimalSeperator,    // Character used to separate whole number from decimal (‘.’ for U.S.)
  bool blnRightJustified     // Right/Left Justification In Buffer
);

--- End code ---

     This function can be used without my String Class as is the case above in Demo2.cpp.  It is also used within my String Class in the various formatting members.  This function’s capabilities are what was missing from my previous posts on removing the C Runtime.  Through use of this function in x86 or x64 we can finally free ourselves from the C Runtime – at least in these demo programs.  As you can see in the program sizes we’re up to 4 or 5 k or so.  Lets take a look at that program using the C Runtime in the typical manner…


--- Code: ---// cl Demo2.cpp /O1 /Os /MT
// 119,808 Bytes x64 ASCII VC19
#include  <stdio.h>
#define x64

int main()
{
 double dblNums[]={123456.78912, -456.9876, 9999.99999, -0.987654, 0.0, 1.985};
 
 printf("i     dblNums[i]\n");
 printf("================\n");
 for(size_t i=0; i<6; i++)
 {
     #ifdef x64
        printf("%-4Iu%12.2f\n",i,dblNums[i]);
     #else
        printf("%-4u%12.2f\n",idblNums[i]);
     #endif
 }
 getchar();

 return 0;
}

#if 0

i     dblNums[i]
================
0      123456.79
1        -456.99
2       10000.00
3          -0.99
4           0.00
5           1.99

#endif

--- End code ---

     So instead of 4 or 5 k we’re at 120 k.  Something worth noting there that’s maybe a mistake on my part is what’s happening with dblNums[5] which is the 1.985 entry in the array.  I did that specifically to stress test my FltToTch algorithm.  I thought that in rounding numbers it was standard practice to round to the nearest even number when the digit place to be rounded is right in the middle, i.e., a ‘5’ digit.  So 1.985 rounded to two digits would cause the ‘5’ to be simply dropped, yielding 1.98.  Alternately, I would have thought 1.975 would be rounded up instead of down because 1.98 is the nearest even number.  At least, that’s how I coded my rounding algorithm in FltToCh.  I can easily change it if someone tells me its wrong.  As you can see, the C Runtime rounded to 1.99 instead of my 1.98.  All the other numbers seem to agree with my function, I believe. 

     The next thing I have to point out about Demo2.cpp is this line…


--- Code: ---extern "C" int _fltused=1;

--- End code ---

     That’s another duzy.  Lost a good many days on that one, and was finally pulled out of the depths of complete and utter defeat by Martins Mozeiko from the Handmade Hero gaming forum.  He’s another one of these incredible folks like Jose Roca here or Michael Mattias in the PowerBASIC Forums who seem to know everything there is to know.  It was Martins’ article on removing the C Runtime where I learned about _fltused.  If you attempt to so much as assign a floating point number to a double without the C Standard Library being loaded your screen will explode in more truly evil looking linker errors than you could ever imagine.  Perhaps at this point I might mention some other issues concerning floating point support, as, like I said, it was nearly a ‘stopper’ on this project, it was that difficult to deal with.

     To begin with, when I first started working on this project through Matt Pietrek’s original LibCTiny work which dates back all the way to the late 1990s, I wasn’t aware of the floating point issue as Matt never mentioned it.  He simply stated that his work might be useful in some small utility programs, but other more complex projects would likely need full use of the C and C++ Standard Libraries.  But he did have nice implementations of printf and sprintf, which are really key functions in the C/C++ coding universe, and I assumed they worked for all variable types – including floating point.  It wasn’t until I was well underway in this project with high hopes of seeing it to completion where I would have full support for both ascii and wide character in both x86 and x64 architectures when I discovered to my complete amazement and dismay that floating points wouldn’t work.  That’s where the _fltused thing enters the picture.  It appears to be some flag sent perhaps between the compiler and linker telling the linker that floating point routines are going to be needed.

     But inserting …


--- Code: ---extern “C” int _fltused=1;

--- End code ---

… into the source code didn’t solve the floating point issue.  All that did was enable a program to compile/link if a double was declared and initialized, such as like so…


--- Code: ---double dblNumber = 3.14159;

--- End code ---

It didn’t allow printf family functions to convert floating points to ascii/wide strings.  That’s where Raymond Filiatreault’s assembler based FpuLib.lib entered the picture.  But that solution only worked for x86 because Raymond didn’t provide an x64 version of the library.  Actually, I believe Raymond’s work on that was done long before x64 even entered the picture.  So in an effort to achieve a solution that would work for both x86 and x64 I coded my own conversion routines which are my FltToCh and FltToWch procedures we used above.  I started that coding work in an x64 environment simply because that was the functionality I lacked.  Since I was writing basic C code it never occurred to me it wouldn’t compile in x86.  I figurred when I finished and got it working in x64 I’d simply recompile in x86 and all would be peaches and cream and I’d have a common solution for both 32 bit ad 64 bit.  Well, that’s not how it worked out!  Not at all!

     Just the other day I ran into an article I hadn’t known about earlier in Dr. Dobb’s Journal by Matthew Wilson, February 1, 2003 entitled “Avoiding The Visual C++ Runtime Library”…

http://www.drdobbs.com/avoiding-the-visual-c-runtime-library/184416623

From him I learned that what I had just accomplished was all but impossible…     


--- Quote ---64-Bit Integers and Floating Points

If you are using floating points in all their glory, then there is no choice but to use the CRT Library, because the complex supporting infrastructure functions and variables reside within it. However, many uses of floating-point numbers are in the fractional intermediate calculations of integral numbers. In these circumstances, it is often possible to emulate the calculation by some clever use of the Win32 MulDiv() function, which multiplies two 32-bit parameters into a 64-bit intermediate and then divides that by the third 32-bit parameter....
...
...
…The 64-bit integer type, __int64, has been a built-in type in the Visual C++ compiler since Version 2.0. Simple operations on the type, including bitwise and logical (Boolean) operations, and addition and subtraction can induce the compiler to place inline bit/byte-wise manipulation. However, the arithmetic operations multiply, divide, and modulo, and the shift operators are all implemented as calls to CRT Library functions (_allmul(), _alldiv(), _allrem(), _allshl(), and _allshr()). If you are using any of those operations, and cannot convert those operations to their 32-bit equivalents without losing accuracy, then you must accept linking to the CRT Library.       

--- End quote ---

     When I attempted to compile my x64 coded and working FltToCh / FltToWch functions in x86 I immediately got bombarded with more compilation and linker errors having to do with functions I never heard of, specifically _dtoui.  That stands for double to unsigned int.  It was precipitated by a casting operation in FltToCh, which involves casting a 64 bit double to a 32 bit unsigned int.  Here’s what it looks like.  The code below is stripping off the leading digits of a double precision number and indexing into the ascii table where ‘48’ starts off as zero, and the expression n = (size_t)x, where n is size_t and x is a double is what is doing it…


--- Code: ---while(k<=17)
{
   if(k == i)
      k++;
   *(p1+k)=48+(char)n;
   x=x*10;
   n = (size_t)x;                //   <<<<<<<<<<<<<<<<<<<<< BANG!
   x = x-n;
   k++;
 }
 *(p1+k) = '\0';

--- End code ---


It just never occurred to me that that was something that wouldn’t compile in x86 but would in x64!  When we are used to working above the assembler level in higher level languages it becomes easy to take things for granted and proceed onward oblivious of what is taking place in the registers.  And that wasn’t the only problem.  The next program - Demo3.cpp, which we havn’t gotten to yet, goes the other way.  In that program I’m converting this…


--- Code: ---TCHAR szBuffer[]=_T("  -12345678987654");

--- End code ---

… to an _int64.  That caused a call to CRT function _allmul() -  one of the functions in the crt Matthew Wilson just described above.  Right at that point I came closest as I’ve ever come to abandoning this whole project.  But then I thought of all the work I put into this, all the successes I had, and all the unbelievable hurdles I had overcame so far, and decided to plow foreward.  Afterall, I did now have an x64 solution to the floating point issue which I just coded and it worked well, and I had Raymond’s FpuLib solution in x86.  So I could hack my way through with that.  However, I did do an internet search on these issues and lo and behold where does that end up?  Back to Martins Mozeiko’s article is where.  At that point I decided it would behoove me to join that community and see if I could get to know Martins Mozeiko, nonwithstanding the fact that I’m far to much of a puritan to have ever played a computer game.  After telling him of my woes he kindly provided me with several options for getting through the _dtoui issue in FltToCh, one of which I was finally able to get to work (see the conditional compilation directives in FltToCh).  The other issue was the CRT functions called by various operations on floating point numbers such as the _allmul(), _alldiv(), allshl(), etc., functions mentioned by Matthew Wilson above.  In Visual Studio 2015 these can be found here in asm form…   

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\i386

In earlier versions of Visual Studio such as my VS 2008 version they are implemented here…

C:\Program Files\Microsoft Visual Studio 9.0\VC\crt\src\intel

Anyway, Martens provided me with his win32_crt_math.cpp file, which you might want to take some note of.  He converted/transferred some of the above assembler into C++ naked functions to take the place of the missing ones in the crt which we haven’t loaded.  And its part of the code running to make this all work in x86.  Just thought you ought to know. 

     Lets move on to Demo3.cpp (and maybe now you’ll have some appreciation of what’s going on behind the scenes to make it work), which shows going the other way, i.e., starting with a numeric character string in ascii or wide format and converting it to binary…


--- Code: ---// cl Demo3.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 3,584 bytes;  34.57 times smaller than with C Std. Lib.
#define UNICODE
#define _UNICODE
#include <windows.h>
#include "stdio.h"
#include "stdlib.h"
#include "tchar.h"

int main()
{
 TCHAR szBuffer[]=_T("  -12345678987654");
 _int64 iNum;
 
 iNum=_ttoi64(szBuffer);
 _tprintf(_T("iNum=%Id\n"), iNum);
 iNum=_abs64(iNum);
 _tprintf(_T("iNum=%Id\n"), iNum);
 getchar();

 return 0;
}
// Output:
// ==================
// iNum=-12345678987654
// iNum=12345678987654

// 12,345,678,987,654
// Twelve Trillion, Three Hundred and Fourty Five Billion, Six Hundred and Seventy Eight Million,
// Nine Hundred and Eighty Seven Thousand, Six Hundred and Fifty Four.

--- End code ---

     We’re only looking at 3.5 k with that.  Note that I defined iNum as an _int64 instead of the size_t or ssize_t I’d typically use.  By doing that the program will provide identical results when compiled in x86 or x64.  Had I used ssize_t, which would be a 32 bit number in x86, the converted number would be too large to fit with an x86 build.  It would wrap around and give bogus results.  Now the fun starts.   Let’s move on to my String Class, which we’re first using in Demo4.cpp…


--- Code: ---// cl Demo4.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 4,096 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out   
// 4,608 Bytes With Full String Class
#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()
{
 String s1(_T("Hello, World!"));
 
 s1.Print(_T("s1         = "), true);
 _tprintf(_T("s1.lpStr() = %s\n"), s1.lpStr());
 getchar();
 
 return 0;
}

--- End code ---

// Output:
// ==========================
// s1         = Hello, World!
// s1.lpStr() = Hello, World!

Note at top with the inclusion of my String Class which necessitates that Strings.cpp be added to the command line compilation string and Strings.h be added to the includes we’re seeing 4,096 bytes in the executable without capabilities for integral numeric conversions, floating point conversions, and formatting (I’ll describe those in a bit), and 4,608 bytes including those capabilities (which, however, aren’t needed in this program).  This is controlled by #defines in Strings.h.  All this program does, by the way, is show use of constructor notation to assign the string L“Hello, World!” to String s1, and output the string to the console two ways, i.e., by String::Print() and through the String::lpStr() member functions.  For comparison purposes here is the same exact program using the C++ Standard Library’s STL based std::wstring class…


--- Code: ---// cl Test3.cpp /O1 /Os /GS- /EHsc kernel32.lib
// 136,192 bytes
#include <string>
#include <cstdio>
#include <tchar.h>

int main()
{
 std::wstring s1(L"Hello, World!");
 
 wprintf(L"s1.c_str() = %s\n", s1.c_str());
 getchar();
 
 return 0;
}

--- End code ---

As you can see, we’re saving about 130,000 bytes here through use of my techniques.  So folks who aren’t members here can have access to this code without the download I’ll now provide the code.  First Strings.h…

continued.....

Frederick J. Harris:

--- Code: ---// 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). NOTE!  Assumes fixed width
#define Strings_h                                               // char/wchar_t 8/16 bit encoding.  Not suitable for Chinese, Japenese or Korean (CJK) encodings with surrigate
                                                                // bytes.
#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 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;
 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 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

--- End code ---

And here would be the Strings.cpp implementation of the String Class found in Strings.h…


--- Code: ---// Strings.cpp
#define UNICODE
#define _UNICODE
#include  <windows.h>
#include  "string.h"
#include  "stdio.h"
#include  "Strings.h"
#include  "tchar.h"


String operator+(TCHAR* lhs, String& rhs)    //global function
{
 String sr=lhs;
 sr=sr+rhs;

 return sr;
}


String::String()
{
 this->lpBuffer=new TCHAR[16];
 this->lpBuffer[0]=0;
 this->iLen=0;
 this->iCapacity=15;
}


String::String(const int iSize, bool blnFillNulls)  //Constructor Creates String With Custom Sized
{                                                   //Buffer (rounded up to paragraph boundary)
 int iNewSize      = (iSize/16+1)*16;
 this->lpBuffer    = new TCHAR[iNewSize];
 this->iCapacity   = iNewSize-1;
 this->iLen        = 0;
 this->lpBuffer[0] = _T('\0');
 if(blnFillNulls)
    memset(this->lpBuffer,0,iNewSize*sizeof(TCHAR));
}


String::String(const TCHAR ch)  //Constructor: Initializes with wchar_t
{
 this->iLen=1;
 int iNewSize=MINIMUM_ALLOCATION;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
}


String::String(const TCHAR* pStr)  //Constructor: Initializes with wchar_t*
{
 this->iLen=_tcslen(pStr);
 int iNewSize=(this->iLen/16+1)*16;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 _tcscpy(lpBuffer,pStr);
}


String::String(const String& s)  //Constructor Initializes With Another String, i.e., Copy Constructor
{
 int iNewSize=(s.iLen/16+1)*16;
 this->iLen=s.iLen;
 this->lpBuffer=new TCHAR[iNewSize];
 this->iCapacity=iNewSize-1;
 _tcscpy(this->lpBuffer,s.lpBuffer);
}


#ifdef INTEGRAL_CONVERSIONS
   String::String(int iNum)
   {
    this->lpBuffer=new TCHAR[16];
    this->iCapacity=15;
    this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
   }

   
   String::String(unsigned int iNum)
   {
    this->lpBuffer=new TCHAR[16];
    this->iCapacity=15;
    this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
   }
   
   
   #ifdef x64
      String::String(size_t iNum)
      {
       this->lpBuffer=new TCHAR[32];
       this->iCapacity=31;
       this->iLen=_stprintf(this->lpBuffer,_T("%Iu"),iNum);
      }


      String::String(ssize_t iNum)
      {
       this->lpBuffer=new TCHAR[32];
       this->iCapacity=31;
       this->iLen=_stprintf(this->lpBuffer,_T("%Id"),iNum);
      }
   #endif
#endif


#ifdef FLOATING_POINT_CONVERSIONS
   String::String(double dblNumber)
   {
    this->lpBuffer=new TCHAR[24];
    this->iCapacity=23;
    this->iLen=FltToTch(this->lpBuffer,dblNumber,24,6,_T('.'),false);
   }
#endif   


String& String::operator=(const TCHAR ch)
{
 this->lpBuffer[0]=ch, this->lpBuffer[1]=_T('\0');
 this->iLen=1;
 return *this;
}


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


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

 return *this;
}


#ifdef INTEGRAL_CONVERSIONS
   #ifdef x64
      String& String::operator=(size_t iNum)
      {
       if(this->iCapacity>=24)
          this->iLen=_stprintf(this->lpBuffer,_T("%Iu"),iNum);
       else
       {
          delete [] this->lpBuffer;
          this->lpBuffer=new TCHAR[24];
          this->iCapacity=23;
          this->iLen=_stprintf(this->lpBuffer,_T("%Iu"),iNum);
       }

       return *this;
      }


      String& String::operator=(ssize_t iNum)
      {
       if(this->iCapacity>=32)
          this->iLen=_stprintf(this->lpBuffer,_T("%Id"),iNum);
       else
       {
          delete [] this->lpBuffer;
          this->lpBuffer=new TCHAR[24];
          this->iCapacity=23;
          this->iLen=_stprintf(this->lpBuffer,_T("%Id"),iNum);
       }

       return *this;
      }
   #endif


   String& String::operator=(int iNum)
   {
    if(this->iCapacity>=15)
       this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
    else
    {
       delete [] this->lpBuffer;
       this->lpBuffer=new TCHAR[16];
       this->iCapacity=15;
       this->iLen=_stprintf(this->lpBuffer,_T("%d"),iNum);
    }

    return *this;
   }


   String& String::operator=(unsigned int iNum)
   {
    if(this->iCapacity>=15)
       this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
    else
    {
       delete [] this->lpBuffer;
       this->lpBuffer=new TCHAR[16];
       this->iCapacity=15;
       this->iLen=_stprintf(this->lpBuffer,_T("%u"),iNum);
    }

    return *this;
   }
#endif


#ifdef FLOATING_POINT_CONVERSIONS
   String& String::operator=(double dblNumber)
   {
    if(this->iCapacity>=24)
       this->iLen=FltToTch(this->lpBuffer,dblNumber,24,6,_T('.'),false);
    else
    {
       delete [] this->lpBuffer;
       this->lpBuffer=new TCHAR[24];
       this->iLen=FltToTch(this->lpBuffer,dblNumber,24,6,_T('.'),false);
       this->iCapacity=23;
    }

    return *this;
   }
#endif
   

String String::operator+(const TCHAR ch)
{
 int iNewLen=this->iLen+1;

 String s(iNewLen,false);
 _tcscpy(s.lpBuffer,this->lpBuffer);
 s.lpBuffer[iNewLen-1]=ch;
 s.lpBuffer[iNewLen]=_T('\0');
 s.iLen=iNewLen;

 return s;
}


String String::operator+(const TCHAR* pStr)
{
 int iNewLen=_tcslen(pStr)+this->iLen;
 String s(iNewLen,false);
 _tcscpy(s.lpBuffer,this->lpBuffer);
 _tcscat(s.lpBuffer,pStr);
 s.iLen=iNewLen;

 return s;
}


String String::operator+(String& strRef)
{
 int iNewLen=strRef.iLen+this->iLen;
 String s(iNewLen,false);
 _tcscpy(s.lpBuffer,this->lpBuffer);
 _tcscat(s.lpBuffer,strRef.lpBuffer);
 s.iLen=iNewLen;

 return s;
}


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


bool String::operator==(const TCHAR* pStr)
{
 if(_tcscmp(this->lpStr(),pStr)==0)
    return true;
 else
    return false;
}


bool String::operator!=(TCHAR* pStr)
{
 if(_tcscmp(this->lpStr(),pStr)==0)
    return false;
 else
    return true;
}


String String::Left(size_t iNum)   //  strncpy = _tcsncpy
{
 if(iNum<this->iLen)
 {
    size_t iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    String sr(iNewSize,true);
    _tcsncpy(sr.lpBuffer,this->lpBuffer,iNum);   // wcsncpy(wchar_t pDest, wchar_t pSource, size_t iCount);
    sr.lpBuffer[iNum]=_T('\0');
    sr.iLen=iNum;
    return sr;
 }
 else
 {
    String sr=*this;
    sr.iLen=this->iLen;
    return sr;
 }
}


String String::Right(size_t iNum)  //Returns Right$(strMain,iNum)
{
 if(iNum<this->iLen)
 {
    size_t iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    String sr(iNewSize,false);
    _tcsncpy(sr.lpBuffer,this->lpBuffer+this->iLen-iNum,iNum);
    sr.lpBuffer[iNum]=_T('\0');
    sr.iLen=iNum;
    return sr;
 }
 else
 {
    String sr=*this;
    sr.iLen=this->iLen;
    return sr;
 }
}


String String::Mid(size_t iStart, size_t iCount)
{
 if(iStart<1)
 {
    String sr;
    return sr;
 }
 if(iCount+iStart>this->iLen)
    iCount=this->iLen-iStart+1;
 String sr(iCount,false);
 _tcsncpy(sr.lpBuffer,this->lpBuffer+iStart-1,iCount);
 sr.lpBuffer[iCount]=_T('\0');
 sr.iLen=iCount;

 return sr;
}


void String::LTrim()
{
 size_t iCt=0;

 for(size_t i=0; i<this->iLen; i++)
 {
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32)
        iCt++;
     else
        break;
 }
 if(iCt)
 {
    for(size_t i=iCt; i<=this->iLen; i++)
        this->lpBuffer[i-iCt]=this->lpBuffer[i];
 }
 this->iLen=this->iLen-iCt;
}


void String::RTrim()
{
 int iCt=0;

 for(int i=this->iLen-1; i>0; i--)
 {
     if(this->lpBuffer[i]==9||this->lpBuffer[i]==10||this->lpBuffer[i]==13||this->lpBuffer[i]==32)
        iCt++;
     else
        break;
 }
 this->lpBuffer[this->iLen-iCt]=0;
 this->iLen=this->iLen-iCt;
}


void String::Trim()
{
 this->LTrim();
 this->RTrim();
}


int String::ParseCount(const TCHAR delimiter)   //returns one more than # of
{                                               //delimiters so it accurately
 int iCtr=0;                                    //reflects # of strings delimited
 TCHAR* p;                                      //by delimiter.

 p=this->lpBuffer;
 while(*p)
 {
   if(*p==delimiter)
      iCtr++;
   p++;
 }

 return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter, size_t iParseCount)
{
 TCHAR* pBuffer=new TCHAR[this->iLen+1];
 if(pBuffer)
 {
    TCHAR* p=pBuffer;
    TCHAR* c=this->lpBuffer;
    while(*c)
    {
       if(*c==delimiter)
          *p=0;
       else
          *p=*c;
       p++, c++;
    }
    *p=0, p=pBuffer;
    for(size_t i=0; i<iParseCount; i++)
    {
        pStr[i]=p;
        p=p+pStr[i].iLen+1;
    }
    delete [] pBuffer;
 }
}


int iMatch(TCHAR* pThis, const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning, int i, int iParamLen)
{
 if(blnCaseSensitive)
 {
    if(_tcsncmp(pThis+i,pStr,iParamLen)==0)   //_tcsncmp
       return i+1;
    else
       return 0;
 }
 else
 {
    if(_tcsnicmp(pThis+i,pStr,iParamLen)==0)  //__tcsnicmp
       return i+1;
    else
       return 0;
 }
}


int String::InStr(const TCHAR* pStr, bool blnCaseSensitive, bool blnStartBeginning)
{
 int i,iParamLen,iRange,iReturn;

 if(*pStr==0)
    return 0;
 iParamLen=_tcslen(pStr);
 iRange=this->iLen-iParamLen;
 if(blnStartBeginning)
 {
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }
 else
 {
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,pStr,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }

 return 0;
}


int String::InStr(const String& s, bool blnCaseSensitive, bool blnStartBeginning)
{
 int i,iParamLen,iRange,iReturn;

 if(s.iLen==0)
    return 0;
 iParamLen=s.iLen;
 iRange=this->iLen-iParamLen;
 if(blnStartBeginning)
 {
    if(iRange>=0)
    {
       for(i=0; i<=iRange; i++)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }
 else
 {
    if(iRange>=0)
    {
       for(i=iRange; i>=0; i--)
       {
           iReturn=iMatch(this->lpBuffer,s.lpBuffer,blnCaseSensitive,blnStartBeginning,i,iParamLen);
           if(iReturn)
              return iReturn;
       }
    }
 }

 return 0;
}


String String::Remove(const TCHAR* pStr) // Individual character removal, i.e., if this contains the
{                                        // English alphabet...
 unsigned int i,j,iStrLen,iParamLen;     //
 TCHAR *pThis, *pThat, *p;               // abcdefghijklmnopqrstuvwxyz
 bool blnFoundBadTCHAR;                  //
                                         // ...and pStr is this...
 iStrLen=this->iLen;                     //
 String sr((int)iStrLen,false);          // "aeiou"
 iParamLen=_tcslen(pStr);                //
 pThis=this->lpBuffer;                   // ,,,then this will be returned in the returned String...
 p=sr.lpStr();                           //
 for(i=0; i<iStrLen; i++)                // bcdfghjklmnpqrstvwxyz
 {                                       //
     pThat=(TCHAR*)pStr;                 // That is, the vowels will be individually removed
     blnFoundBadTCHAR=false;
     for(j=0; j<iParamLen; j++)
     {
         if(*pThis==*pThat)
         {
            blnFoundBadTCHAR=true;
            break;
         }
         pThat++;
     }
     if(!blnFoundBadTCHAR)
     {
        *p=*pThis;
        p++;
        *p=_T('\0');
     }
     pThis++;
 }
 sr.iLen=_tcslen(sr.lpStr());

 return sr;
}


String String::Remove(const TCHAR* pMatch, bool blnCaseSensitive)
{
 size_t i,iCountMatches=0,iCtr=0;

 size_t iLenMatch=_tcslen(pMatch);
 for(i=0; i<this->iLen; i++)
 {
     if(blnCaseSensitive)
     {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)  //_tcsncmp
           iCountMatches++;
     }
     else
     {
        if(_tcsnicmp(lpBuffer+i,pMatch,iLenMatch)==0) //__tcsnicmp
           iCountMatches++;
     }
 }
 int iAllocation=this->iLen-(iCountMatches*iLenMatch);
 String sr(iAllocation,false);
 for(i=0; i<this->iLen; i++)
 {
     if(blnCaseSensitive)
     {
        if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           i+=iLenMatch-1;
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
     }
     else
     {
        if(_tcsnicmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
           i+=iLenMatch-1;
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
     }
 }
 sr.iLen=iCtr;
 return sr;
}


String String::Replace(TCHAR* pMatch, TCHAR* pNew)  //strncmp = _tcsncmp
{
 size_t i,iLenMatch,iLenNew,iCountMatches,iExtra,iExtraLengthNeeded,iAllocation,iCtr;
 iLenMatch=_tcslen(pMatch);
 iCountMatches=0, iAllocation=0, iCtr=0;
 iLenNew=_tcslen(pNew);
 if(iLenNew==0)
 {
    String sr=this->Remove(pMatch,true); //return
    return sr;
 }
 else
 {
    iExtra=iLenNew-iLenMatch;
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(lpBuffer+i,pMatch,iLenMatch)==0)
           iCountMatches++;  //Count how many match strings
    }
    iExtraLengthNeeded=iCountMatches*iExtra;
    iAllocation=this->iLen+iExtraLengthNeeded;
    String sr(iAllocation,false);
    for(i=0; i<this->iLen; i++)
    {
        if(_tcsncmp(this->lpBuffer+i,pMatch,iLenMatch)==0)
        {
           _tcscpy(sr.lpBuffer+iCtr,pNew);
           iCtr+=iLenNew;
           i+=iLenMatch-1;
        }
        else
        {
           sr.lpBuffer[iCtr]=this->lpBuffer[i];
           iCtr++;
        }
        sr.lpBuffer[iCtr]=_T('\0');
    }
    sr.iLen=iCtr;
    return sr;
 }
}


#ifdef FORMATTING
   void String::Format(double dblNumber, size_t iArraySize, size_t iDecPlaces, TCHAR cDecimalSeperator, bool blnRightJustified)
   {
    if(this->iCapacity<iArraySize-1)
    {
       delete [] this->lpBuffer;
       this->lpBuffer=new TCHAR[iArraySize];
       this->iCapacity=iArraySize-1;
    }   
    this->iLen=FltToTch(this->lpBuffer,dblNumber,iArraySize,iDecPlaces,cDecimalSeperator,blnRightJustified);
   }

   
   void String::Format(double dblNumber, size_t iArraySizeCountOfObjects, size_t iDecimalPlaces, TCHAR cThousandsSeperator, TCHAR cDecimalSeperator, bool blnRightJustified)
   {
    size_t iLen=0,iPadding=0;
    int iDecPt=0;
    _int64 iInt;
   
    if(this->iCapacity<iArraySizeCountOfObjects-1)
    {
       delete [] this->lpBuffer;
       size_t iNewSize=(iArraySizeCountOfObjects/16+1)*16;
       this->lpBuffer=new TCHAR[iNewSize];
       this->iCapacity=iNewSize-1;
       this->lpBuffer[0]=0;
    }
    String s1(24,true);
    s1.Format(dblNumber,24,iDecimalPlaces,cDecimalSeperator,false);
    s1.Trim();
    if(iDecimalPlaces)
    {
       iDecPt=s1.InStr(cDecimalSeperator,false,false);
       String s2=s1.Left(iDecPt-1);
       iInt=_ttoi64(s2.lpStr());
    }
    else
       iInt=_ttoi64(s1.lpStr());
    String s3(28,true);
    if(iInt==0 && dblNumber<0.0)
       s3=_T("-0");
    else
       s3.Format((ssize_t)iInt,28,cThousandsSeperator,false);
    s3.Trim();
    if(iDecimalPlaces)
       s3=s3+s1.Mid(iDecPt,iDecimalPlaces+1);
    iLen=s3.Len();
    if(iLen>this->iCapacity)
       return;
    iPadding=iArraySizeCountOfObjects-iLen-1;
    if(blnRightJustified)
    {
       for(size_t i=0; i<iPadding; i++)
           this->lpBuffer[i]=32;
       this->lpBuffer[iPadding]=0;
       _tcscat(this->lpBuffer, s3.lpStr()); 
    }
    else
    {
       _tcscpy(this->lpBuffer,s3.lpStr());
       for(size_t i=iLen; i<iArraySizeCountOfObjects-1; i++)
           this->lpBuffer[i]=32;
       this->lpBuffer[iArraySizeCountOfObjects-1]=0;   
    }
    this->iLen=_tcslen(this->lpBuffer);           
   }
 
   
   void String::Format(ssize_t iNumber, size_t iArraySizeCountOfObjects, TCHAR cThousandsSeperator, bool blnRightJustified)
   {
    bool blnPositive;
    TCHAR szBuf1[28];
    TCHAR szBuf2[28];
    size_t iDigit=0;
    size_t iLen=0;
    size_t iCtr=1;
    size_t j=0;

    memset(szBuf1,0,28*sizeof(TCHAR));
    memset(szBuf2,0,28*sizeof(TCHAR));
    if(iNumber<0)
       blnPositive=false;
    else
       blnPositive=true;
    #ifdef x64
       iNumber=_abs64(iNumber);
    #else
       iNumber=abs(iNumber);
    #endif
    #ifdef x64
       _stprintf(szBuf1,_T("%Iu"),(size_t)iNumber);
    #else
       _stprintf(szBuf1,_T("%u"),(size_t)iNumber);
    #endif
    _tcsrev(szBuf1);
    iLen=_tcslen(szBuf1);
    for(size_t i=0; i<iLen; i++)
    {
        if(iCtr==3)
        {
           iDigit++;
           szBuf2[j]=szBuf1[i];
           if(iDigit<iLen)
           {
              j++;
              szBuf2[j]=cThousandsSeperator;
           }
           j++, iCtr=1;
        }
        else
        {
           iDigit++;
           szBuf2[j]=szBuf1[i];
           j++, iCtr++;
        }
    }
    _tcsrev(szBuf2); // Done With Creating String With Commas
    memset(szBuf1,0,28*sizeof(TCHAR));  // Reuse szBuf1
    if(blnPositive)
       _tcscpy(szBuf1,szBuf2);
    else
    {
       szBuf1[0]=_T('-');
       _tcscat(szBuf1,szBuf2);
    }   
    size_t iRequiredBytes;         // Find out which of two is larger - length of string necessary
    iLen=_tcslen(szBuf1);          // or iFldLen
    if(iArraySizeCountOfObjects<=iLen)
       return;
    if(iArraySizeCountOfObjects>(int)iLen)
       iRequiredBytes=iArraySizeCountOfObjects;
    else
       iRequiredBytes=iLen;
    if(iRequiredBytes>(size_t)this->iCapacity)
    {
       delete [] this->lpBuffer;
       int iNewSize=(iRequiredBytes*EXPANSION_FACTOR/16+1)*16;
       this->lpBuffer=new TCHAR[iNewSize];
       this->iCapacity=iNewSize-1;
    }
    memset(this->lpBuffer,0,this->iCapacity*sizeof(TCHAR)); 
    ssize_t iDifference=iArraySizeCountOfObjects-iLen-1;
    if(blnRightJustified)                                             
    {
       if(iDifference > 0)
       {
          for(size_t i=0; i<(size_t)iDifference; i++)
              this->lpBuffer[i]=_T(' ');  // 32
       }
       _tcscat(this->lpBuffer,szBuf1);
    }
    else
    {
       _tcscpy(this->lpBuffer,szBuf1);
       if(iDifference>0)
       {
          for(size_t i=iLen; i<iDifference+iLen; i++)
              this->lpBuffer[i]=_T(' ');  // 32
       }
    }
    this->iLen=_tcslen(this->lpBuffer);
   }
#endif


TCHAR* String::lpStr()
{
 return this->lpBuffer;
}


size_t String::Len(void)
{
 return this->iLen;
}


size_t String::Capacity(void)
{
 return this->iCapacity;
}


#ifdef CONSOLE_OUTPUT
   void String::Print(bool blnCrLf)
   {
    _tprintf(_T("%s"),this->lpBuffer);
    if(blnCrLf)
       _tprintf(_T("\n"));
   }

   
   void String::Print(const TCHAR* pStr, bool blnCrLf)
   {
    _tprintf(_T("%s%s"),pStr,lpBuffer);
    if(blnCrLf)
       _tprintf(_T("\n"));
   }
#endif


String::~String()
{
 delete [] this->lpBuffer;
}
// End Strings.cpp

--- End code ---


continued...

Frederick J. Harris:
     In some of my previous writings here I described one of the downsides of having one’s own String Class as being the fact that it is always changing.  The above code is witness to that.  This incarnation of my string class is again different from any others I have posted.  I’m always working on it.  Since this floating point issue bedeviled me so, I ended up reworking my formatting functions to improve them from what I had before only recently a few weeks ago, and I ended up removing two String::Money() members which were simply special cases of my other formatting members where only two digits to the right of the decimal point were required.  Also, I added the capability for the user to specify which symbol is to be used for decimal points and thousands separators.

     Now here’s the scoop on some defines you’ll see in both Strings.h and Strings.cpp.  Since I take such great pleasure in minimizing the sizes of my executables it has never been lost on me that some of my projects where I use my String Class use certain portions of it only, while other projects use different portions, i.e., member functions.  Related to this, when one is working with various utulity code the thought often crosses one’s mind that the code being worked on might be a canidate for inclusion in my String Class.  Of course, all this can cause the String Class to grow and grow and arrive at a point where no particular project uses all of it.  My solution to this was to include various ‘chunks’ of code within #ifdef conditional compilation blocks, so that I could eliminate certain portions of code or functionality in my String Class if not needed in some given project.  And these have changed over the years.  What I presently have now are these…

#define INTEGRAL_CONVERSIONS                                   

This allows direct assignment of integral numeric values to Strings through the operator= overload, or conctructor notation, e.g., String s=54321, or String s(12345).  It can be thought of analgous to PowerBASIC’s Str$() function.  The C++ Standard Library isn’t set up to do this, I don’t believe.  Not that mine’s better for doing it, but rather just different.

//#define FLOATING_POINT_CONVERSIONS                             

This is the same as above but for dealing with floating point numbers.  It allows direct assignment of floating point values to Strings, e.g., String s(3.14159) or String s = -98765.43210. 

//#define FORMATTING                                             

This is kind of a replacement for PowerBASIC’s Format$() function.  There are three overloaded format members like so…


--- Code: ---#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

--- End code ---

The bottom one takes an integral 1st parameter and the upper two doubles.  With these you can line up decimal points (or comma seperators, which are used in Europe), include commas every three digits, specify left/right justification within a field width you specify, specify desired number of decimal places, etc.

     So in the following programs I’ll list program sizes with and without these various capabilities.  Obviously, if a program is using floating point numbers that capability needs to be brought in through the above defines.  So given that explanation, lets move to Demo5.cpp…


--- Code: ---// cl Demo5.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 4,608 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 5,120 Bytes With Full String Class
#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()
{
 int iParseCount=0;
 String* pStrs=NULL;
 String s1;
 
 s1=_T("Zero, One, Two, Three, Four, Five");
 s1.Print(_T("s1 = "),true);
 iParseCount=s1.ParseCount(_T(','));
 _tprintf(_T("iParseCount = %d\n\n"),iParseCount);
 pStrs=new String[iParseCount];
 s1.Parse(pStrs, _T(','), iParseCount);
 for(int i=0; i<iParseCount; i++)
 {
     pStrs[i].LTrim();
     pStrs[i].Print(true);
 }
 delete [] pStrs;
 getchar();
 
 return 0;
}

// Output:
// ==========================
// s1 = Zero, One, Two, Three, Four, Five
// iParseCount = 6

// Zero
// One
// Two
// Three
// Four
// Five

--- End code ---


     Its just a parsing example using my String::Parse() member, which is similar to PowerBASIC’s Parse statement.  Quite a few significant things to discuss here.  We’ll start with this strange creature in the command line compilation string….


--- Code: ---/Zc:sizedDealloc-

--- End code ---

The ‘/Zc:sizedDealloc-‘ switch is required by VC19 (from Visual Studio 2015), but is not required by earlier versions.  It is essentially a ‘workaround’ for a VC19 bug which Microsoft has not as of this writing fixed although they’ve acknolowedged it.  The switch turns off the newest changes to the C++ ‘Placement New’ capabilities of the newest C++ Standard.  What triggers the need for it in this program are these lines…


--- Code: ---String* pStrs=NULL;
delete [] pStrs;

--- End code ---

…which are somewhat unusual in that none of my other String Class members other than String::Parse() take as parameters pointers to String Class objects.  I lost a lot of days on this one before I solved it.  So we’re somewhere around 5 k with that code.  Lets take a look at the STL based C++ Standard Library weird science version of the above 


--- Code: ---// cl StdLibParse1.cpp /O1 /Os /MT /EHsc
// 200,192 Bytes
#include <iostream>
#include <sstream>

int main()
{
 std::string input = "Zero, One, Two, Three, Four, Five, Six";
 std::istringstream ss(input);
 std::string token;
 while(std::getline(ss, token, ','))
 {
    std::cout << token << '\n';
 }
 
 return 0;
}


#if 0

Output:
=======
Zero
 One
 Two
 Three
 Four
 Five
 Six

 #endif

--- End code ---

That’s coming in 200 k or 195 k bigger than with TCLib.lib.  Moving on, Demo6.cpp shows use of one of the overloaded constructors to construct a new String out of an existing String…


--- Code: ---// cl Demo6.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 3,584 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 4,096 Bytes With Full String Class
#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()
{
 String s1(_T("Hello, World!"));
 String s2(s1);
 
 s1.Print(true);
 s2.Print(true);
 getchar();
 
 return 0;
}

// Output:
// =============
// Hello, World!
// Hello, World!

     String s1 is instantiated/constructed with the text string literal “Hello, World!”.  String s2 is constructed out of String s1.  Lets play with some big numbers again.  Here is Demo7.cpp where we’ll use some of the overloaded String Constructors to create Strings out of numbers…

[CODE]
// cl Demo7.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 5,632 Bytes Bytes With Full String Class
#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()
{
 String s1(123456789);
 s1.Print(true);
 String s2(-123456789);
 s2.Print(true);
 #ifdef x64
    String s3(12345678987654);
    String s4(-12345678987654);
    s3.Print(true);
    s4.Print(true);
 #endif
 String s5(-3.14159);
 s5.Print(true);
 getchar();
 
 return 0;
}

// Output:
// =============
// 123456789
// -123456789
// 12345678987654
// -12345678987654
// -3.141590

--- End code ---


     Note that I had to put String s3 and s4 inside conditional compilation directives because I didn’t create an overloaded constructor for _int64 when compiling x86 mode.  Its an addition I should probably make (yet another version of my Strig Class! – See what I mean?).  Demo8.cpp is more of the same, but we’ll start getting into the operator= functions.  Here we construct a String object with a TCHAR and use operator=…


--- Code: ---// cl Demo8.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 4,096 Bytes  With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 4,608 Bytes With Full String Class
#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()
{
 String s1,s2,s3;
 
 s1=_T('A');
 s2=_T("Hello, World!");
 s3=s2;
 s1.Print(true);
 s2.Print(true);
 s3.Print(true);
 getchar();
 
 return 0;
}

// Output:
// =============
// A
// Hello, World!
// Hello, World!

--- End code ---

In Demo9.cpp we’ll play with big numbers again, but use operator= overloads to assign the numbers to String objects…


--- Code: ---// cl Demo9.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 6,144 Bytes
#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()
{
 String s1,s2,s3,s4,s5;
 
 s1 =  123456789;
 s1.Print(true);
 s2 = -123456789;
 s2.Print(true);
 #ifdef x64
    s3 =  12345678987654;
    s4 = -12345678987654;
    s3.Print(true);
    s4.Print(true);
 #endif   
 s5 =  3.141590;
 s5.Print(true);
 getchar();
 
 return 0;
}

// Output:
// =============
// 123456789
// -123456789
// 12345678987654
// -12345678987654
// 3.141590

--- End code ---

Demo10.cpp is a fun little program where we’ll start getting into operator+ overloads…


--- Code: ---// cl Demo10.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 4,096 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 4,608 Bytes With Full String Class
#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()
{
 String s1;
 
 for(size_t i=65; i<=90; i++)
 {
     s1=s1+i;
     s1.Print(true);
 }
 getchar();
 
 return 0;
}

// Output:
// =============
// A
// AB
// ABC
// ABCD
// ABCDE
// ABCDEF
// ABCDEFG
// ABCDEFGH
// ABCDEFGHI
// ABCDEFGHIJ
// ABCDEFGHIJK
// ABCDEFGHIJKL
// ABCDEFGHIJKLM
// ABCDEFGHIJKLMN
// ABCDEFGHIJKLMNO
// ABCDEFGHIJKLMNOP
// ABCDEFGHIJKLMNOPQ
// ABCDEFGHIJKLMNOPQR
// ABCDEFGHIJKLMNOPQRS
// ABCDEFGHIJKLMNOPQRST
// ABCDEFGHIJKLMNOPQRSTU
// ABCDEFGHIJKLMNOPQRSTUV
// ABCDEFGHIJKLMNOPQRSTUVW
// ABCDEFGHIJKLMNOPQRSTUVWX
// ABCDEFGHIJKLMNOPQRSTUVWXY
// ABCDEFGHIJKLMNOPQRSTUVWXYZ

--- End code ---

Demo11.cpp is kind of fun too, as we’ll disassemble a String with Parse, and then put it back together again from its disassembled parts with operator+ calls on String objects.  And we’ll be able to make use of good ‘ol Right$ ( String::Right() ) in the process… 


--- Code: ---// cl Demo11.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 6,144 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 6,656 Bytes With Full Stri ng Class
#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"

int main()
{
 String* pStrs=NULL;
 int iParseCount=0;
 String s1,s2;
 
 s1=_T("Zero,  One, Two  , Three ,  Four, Five   , Six, Seven, Eight , Nine, Ten");
 s1.Print(_T("s1 = "), true);
 iParseCount = s1.ParseCount(_T(','));
 _tprintf(_T("iParseCount = %d\n\n"),iParseCount);
 pStrs = new String[iParseCount];
 s1.Parse(pStrs, _T(','), iParseCount);
 for(int i=0; i<iParseCount; i++)
 {
     pStrs[i].Trim();
     pStrs[i].Print(true);
     s2 = s2 + _T(',') + pStrs[i];
 }
 s2.Print(_T("\ns2 = "),true);
 s2=s2.Right(s2.Len()-1);
 s2.Print(_T("s2 = "),true);
 delete [] pStrs;
 getchar();
 
 return 0;
}

// Output:
// =============
// s1 = Zero,  One, Two  , Three ,  Four, Five   , Six, Seven, Eight , Nine, Ten
// iParseCount = 11
//
// Zero
// One
// Two
// Three
// Four
// Five
// Six
// Seven
// Eight
// Nine
// Ten
//
// s2 = ,Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten
// s2 = Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten

--- End code ---

Demo12.cpp makes use of good old InStr() in C++ String Class form….


--- Code: ---// cl Demo12.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 5,120 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 5,632 Bytes With Full Stri ng Class
#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()
{
 int iFound=0;
 String s1;
 
 s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
 s1.Print(_T("s1 = "),true);
 iFound=s1.InStr(_T("real"),true,true);
 if(!iFound)
    _tprintf(_T("Couldn't Locate 'real' In s1 With A Case Sensitive Search!\n"));
 iFound=s1.InStr(_T("real"),false,true); 
 _tprintf(_T("But A Case Insensitive Search Located 'real' At One Based Offset %d In s1!\n"),iFound);
 getchar();
 
 return 0;
}

// Output:
// ==========================================================================
// s1 = C++ Can Be A Real Pain In The Butt To Learn!
// Couldn't Locate 'real' In s1 With A Case Sensitive Search!
// But A Case Insensitive Search Located 'real' At One Based Offset 14 In s1!

--- End code ---

Demo13.cpp makes use of one of the two overloads of String::Remove() where the vowels “aeiou” are individually removed from the String containing the English alphabet….


--- Code: ---// cl Demo13.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 5,120 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 5,120 Bytes With Full String Class
#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()
{
 String s1=_T("abcdefghijklmnopqrstuvwxyz");
 String s2=_T("aeiou");
 String s3=s1.Remove(s2.lpStr());
 s1.Print(true);
 s3.Print(true);
 getchar();
 
 return 0;
}

// Output:
// ==========================
// abcdefghijklmnopqrstuvwxyz
// bcdfghjklmnpqrstvwxyz

--- End code ---

Demo14.cpp uses the other String::Remove() overload to remove the sub-string “Real” from a larger String…


--- Code: ---// cl Demo14.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 5,120 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
// 5,632 Bytes With Full String Class
#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()
{
 String s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
 s1.Print(true);
 String s2=s1.Remove(_T("Real"), true);
 s2.Print(true);
 getchar();
 
 return 0;
}

// Output:
// ============================================
// C++ Can Be A Real Pain In The Butt To Learn!
// C++ Can Be A  Pain In The Butt To Learn!

--- End code ---

We’re back to big numbers again with Demo15.cpp, where we’ll make first use of one of my String::Format() members which converts a large double precision floating point number to a String with comma separators for thousands places, and a dot for the decimal place seperator…


--- Code: ---// cl Demo15.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 9,216 Bytes With Full String Class, Which Is Mostly Needed
#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 _tmain()
{
 double dblNum=-1234567898786.7898765;
 String s1;
 
 s1.Format(dblNum, 24, 3, _T(','), _T('.'), true);
 s1.Print(_T("s1="),true);
 getchar();
 
 return 0;
}

// Output:
// ==========================
// s1= -1,234,567,898,786.790

--- End code ---

My last example, Demo16.cpp, uses my String::Format() member in a double For Loop to iterate through an array of doubles and format the numbers with zero, one or two decimal places to right of decimal.  I added a number to the array to test the rounding when the rounding digit is a five.  So both of the last two numbers in the array, that is, 1.985 and 1.9754321 both round to 1.98 when two digits are specified to the right of the decimal.  I thought this was how it was supposed to be done.  But I’m not an expert on this matter.  If someone tells me differently I can easily change the code…


--- Code: ---// cl Demo16.cpp Strings.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib
// 9,728 Bytes With Full String Class, Which Is Mostly Needed
#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 _tmain()
{
 double dblNums[]={123456.78912, -456.9876, 9999.99999, -0.987654, 0.0, 1.985, 1.9754321};
 String s1;
 
 for(size_t i=0; i<=2; i++)
 {
     for(size_t j=0; j<sizeof(dblNums)/sizeof(dblNums[0]); j++)
     {
         s1.Format(dblNums[j], 12, i, _T(','), _T('.'), true);
         s1.Print(true);
     }
     printf("\n\n");
 }
 getchar();
 
 return 0;
}

//     Output
// ==========
//    123,457
//       -457
//     10,000
//         -1
//          0
//          2
//          2
//
//
//  123,456.8
//     -457.0
//   10,000.0
//       -1.0
//        0.0
//        2.0
//        2.0
//
//
// 123,456.79
//    -456.99
//  10,000.00
//      -0.99
//       0.00
//       1.98
//       1.98

--- End code ---

     Well, that’s all the little demos.  Its time to post all the source code necessary to build TCLib.lib…

continued...

Frederick J. Harris:

--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_con_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
extern "C" int __cdecl  main();

extern "C" void __cdecl mainCRTStartup()
{
 int iReturn = main();
 ExitProcess(iReturn);
}

--- End code ---



--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_con_w.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
extern "C" int __cdecl  wmain();

extern "C" void __cdecl wmainCRTStartup()
{
 int iReturn = wmain();
 ExitProcess(iReturn);
}

--- End code ---



--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);

extern "C" void __cdecl WinMainCRTStartup(void)
{
 int iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
 ExitProcess(iReturn);
}

--- End code ---


--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_w.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int);

extern "C" void __cdecl wWinMainCRTStartup(void)
{
 int iReturn = wWinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
 ExitProcess(iReturn);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memset.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include "memory.h"

void* __cdecl memset(void* p, int c, size_t count)
{
 char* pCh=(char*)p;
 for(size_t i=0; i<count; i++)
     pCh[i]=(char)c;
 return p;
}

wchar_t* __cdecl wmemset(wchar_t* p, wchar_t c, size_t count)
{
 for(size_t i=0; i<count; i++)
     p[i]=c;
 return p;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//
//                          LIBCTINY -- Matt Pietrek 2001
//                           MSDN Magazine, January 2001
//
//                              With Help From Mike_V
//                       
//                           By Fred Harris, January 2016
//
//                    cl newdel.cpp /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>

void* __cdecl operator new(size_t s)
{
 return HeapAlloc(GetProcessHeap(), 0, s);
}

void  __cdecl operator delete(void* p)
{
 HeapFree(GetProcessHeap(), 0, p);
}

void* operator new [] (size_t s)
{
 return HeapAlloc(GetProcessHeap(), 0, s);
}

void operator delete [] (void* p)
{
 HeapFree(GetProcessHeap(), 0, p);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                    cl printf.cpp /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include <stdarg.h>
#include "stdio.h"
#pragma comment(linker, "/defaultlib:user32.lib") 


int __cdecl printf(const char* format, ...)
{
 char szBuff[1024];
 DWORD cbWritten;
 va_list argptr;
 int retValue;
         
 va_start(argptr, format);
 retValue = wvsprintf(szBuff, format, argptr);
 va_end(argptr);
 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), szBuff, retValue, &cbWritten, 0);

 return retValue;
}


int __cdecl wprintf(const wchar_t* format, ...)
{
 wchar_t szBuffW[1024];
 char szBuffA[1024];
 int iChars,iBytes;
 DWORD cbWritten;
 va_list argptr;
           
 va_start(argptr, format);
 iChars = wvsprintfW(szBuffW, format, argptr);
 va_end(argptr);
 iBytes=WideCharToMultiByte(CP_ACP,0,szBuffW,iChars,szBuffA,1024,NULL,NULL);
 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), szBuffA, iBytes, &cbWritten, 0);

 return iChars;
}

--- End code ---


--- Code: ---//=============================================================
//   Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                By Fred Harris, February 2016
//
// cl sprintf.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN   
//=============================================================
// cl sprintf.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdarg.h>
#include "stdio.h"
#define EOF (-1)
#pragma comment(linker, "/defaultlib:user32.lib")

int __cdecl sprintf(char* buffer, const char* format, ...)
{
 va_list argptr;
 int retValue;
           
 va_start(argptr, format);
 retValue = wvsprintfA(buffer, format, argptr);
 va_end(argptr);

 return retValue;
}

int __cdecl swprintf(wchar_t* buffer, const wchar_t* format, ...)
{
 va_list argptr;
 int retValue;
         
 va_start(argptr, format);
 retValue = wvsprintfW(buffer, format, argptr);
 va_end(argptr);

 return retValue;
}

--- End code ---



--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//     cl _strnicmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl _strnicmp(const char* str1, const char* str2, size_t count)
{
 size_t iLen1=strlen(str1);
 size_t iLen2=strlen(str2);
 if(count>iLen1)
    return -1;
 if(count>iLen2)
    return 1;
 char* pStr1=(char*)malloc(count+1);
 char* pStr2=(char*)malloc(count+1);
 strncpy(pStr1,str1,count);
 strncpy(pStr2,str2,count);
 pStr1[count]=0;
 pStr2[count]=0;
 int iReturn=lstrcmpiA(pStr1,pStr2);
 free(pStr1);
 free(pStr2);

 return iReturn;
}

int __cdecl _wcsnicmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
 size_t iLen1=wcslen(str1);
 size_t iLen2=wcslen(str2);
 if(count>iLen1)
    return -1;
 if(count>iLen2)
    return 1;
 wchar_t* pStr1=(wchar_t*)malloc(count*2+2);
 wchar_t* pStr2=(wchar_t*)malloc(count*2+2);
 wcsncpy(pStr1,str1,count);
 wcsncpy(pStr2,str2,count);
 pStr1[count]=0;
 pStr2[count]=0;
 int iReturn=lstrcmpiW(pStr1,pStr2);
 free(pStr1);
 free(pStr2);

 return iReturn;
}

--- End code ---


--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                 cl strncpy.cpp /O1 /Os /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"


char* __cdecl strncpy(char* strDest, const char* strSource, size_t iCount)   
{                                              // 3rd param is size_t
 size_t iLenSrc=strlen(strSource);             // strlen returns size_t                                           
 lstrcpynA(strDest,strSource,(int)iCount);     // lstrcpyn wants an int here for 3rd param                                           
 strDest[iCount-1]=strSource[iCount-1];        // so try cast
 if(iCount>iLenSrc)
 {
    for(size_t i=iLenSrc; i<iCount; i++)
        strDest[i]=0;
 }

 return strDest;
}


wchar_t* __cdecl wcsncpy(wchar_t* strDest, const wchar_t* strSource, size_t iCount)   
{                                           // 3rd param is size_t
 size_t iLen=wcslen(strSource);             // strlen returns size_t                                           
 lstrcpynW(strDest,strSource,(int)iCount);  // lstrcpyn wants an int here for 3rd param                                           
 strDest[iCount-1]=strSource[iCount-1];     // so try cast
 if(iCount>iLen)
 {
    for(size_t i=iLen; i<iCount; i++)
        strDest[i]=0;
 }

 return strDest;


--- End code ---


--- Code: ---//===============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//       cl strncmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"


int __cdecl strncmp(const char* str1, const char* str2, size_t count)
{
 size_t iLen1=strlen(str1);
 size_t iLen2=strlen(str2);
 if(count>iLen1)
    return -1;
 if(count>iLen2)
    return 1;
 char* pStr1=(char*)malloc(count+1);
 char* pStr2=(char*)malloc(count+1);
 strncpy(pStr1,str1,count);
 strncpy(pStr2,str2,count);
 pStr1[count]=0;
 pStr2[count]=0;
 int iReturn=strcmp(pStr1,pStr2);
 free(pStr1);
 free(pStr2);

 return iReturn;
}


int __cdecl wcsncmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
 size_t iLen1=wcslen(str1);
 size_t iLen2=wcslen(str2);
 if(count>iLen1)
    return -1;
 if(count>iLen2)
    return 1;
 wchar_t* pStr1=(wchar_t*)malloc(count*2+2);
 wchar_t* pStr2=(wchar_t*)malloc(count*2+2);
 wcsncpy(pStr1,str1,count);
 wcsncpy(pStr2,str2,count);
 pStr1[count]=0;
 pStr2[count]=0;
 int iReturn=wcscmp(pStr1,pStr2);
 free(pStr1);
 free(pStr2);

 return iReturn;
}

--- End code ---


--- Code: ---//========================================================
// Developed As An Addition To Matt Pietrek's LibCTiny.lib
//              By Fred Harris, January 2016
//
//     cl _strrev.cpp /Oi /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================
#include <windows.h>
#include "string.h"

char* __cdecl _strrev(char* pStr)
{
 size_t iLen,iHalf;
 char a,b;

 iLen=strlen(pStr), iHalf=iLen/2;
 for(size_t i=0; i<iHalf; i++)
 {
     a=pStr[i], b=pStr[iLen-i-1];
     pStr[i]=b, pStr[iLen-i-1]=a;
 }

 return pStr;
}

wchar_t* __cdecl _wcsrev(wchar_t* pStr)
{
 size_t iLen,iHalf;
 wchar_t a,b;

 iLen=wcslen(pStr), iHalf=iLen/2;
 for(size_t i=0; i<iHalf; i++)
 {
     a=pStr[i], b=pStr[iLen-i-1];
     pStr[i]=b, pStr[iLen-i-1]=a;
 }

 return pStr;
}

--- End code ---



--- Code: ---//===================================================================
//   Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                By Fred Harris, January 2016
//
//         cl strcat.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//===================================================================
#include <windows.h>
#include "string.h"

char* __cdecl strcat(char* strDest, const char* strSource)   
{                                           // 3rd param is size_t
 return lstrcatA(strDest, strSource);
}

wchar_t* __cdecl wcscat(wchar_t* strDest, const wchar_t* strSource)   
{                                           // 3rd param is size_t
 return lstrcatW(strDest, strSource);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strcmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

int __cdecl strcmp(const char* string1, const char* string2)   
{
 return lstrcmpA(string1,string2);
}

int __cdecl wcscmp(const wchar_t* string1, const wchar_t* string2)   
{
 return lstrcmpW(string1,string2);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
// cl strcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>

char* __cdecl strcpy(char* strDestination, const char* strSource)
{
 return lstrcpyA(strDestination, strSource);
}

wchar_t* __cdecl wcscpy(wchar_t* strDestination, const wchar_t* strSource)
{
 return lstrcpyW(strDestination, strSource);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strlen.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

size_t __cdecl strlen(const char* strSource)
{
 return lstrlenA(strSource);
}

size_t __cdecl wcslen(const wchar_t* strSource)
{
 return lstrlenW(strSource);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//              cl getchar.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "stdio.h"

char __cdecl getchar()
{
 DWORD nRead,dwConsoleMode;
 INPUT_RECORD ir[8];
 bool blnLoop=true;
 HANDLE hStdIn;
 char c=0;

 hStdIn=GetStdHandle(STD_INPUT_HANDLE);
 GetConsoleMode(hStdIn,&dwConsoleMode);
 SetConsoleMode(hStdIn,0);
 FlushConsoleInputBuffer(hStdIn);
 do
 {
    WaitForSingleObject(hStdIn,INFINITE);
    ReadConsoleInput(hStdIn,ir,8,&nRead);
    for(unsigned i=0;i<nRead;i++)
    {
        if(ir[i].EventType==KEY_EVENT && ir[i].Event.KeyEvent.bKeyDown==TRUE)
        {
           c=ir[i].Event.KeyEvent.uChar.AsciiChar;
           blnLoop=false;
        }
    }
 }while(blnLoop==true);
 SetConsoleMode(hStdIn,dwConsoleMode);

 return c;
}

--- End code ---


--- Code: ---// cl alloc.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#include "malloc.h"

void* __cdecl malloc(size_t size)
{
 return HeapAlloc(GetProcessHeap(), 0, size);
}

void __cdecl free(void* pMem)
{
 HeapFree(GetProcessHeap(), 0, pMem);
}

--- End code ---


--- Code: ---// cl alloc2.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#include "malloc.h"

void* __cdecl realloc(void* pMem, size_t size)
{
 if(pMem)
    return HeapReAlloc(GetProcessHeap(), 0, pMem, size);
 else
    return HeapAlloc(GetProcessHeap(), 0, size);
}

void* __cdecl calloc(size_t nitems, size_t size)
{
 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nitems * size);
}

--- End code ---


--- Code: ---// cl allocsup.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#include "malloc.h"

void* __cdecl _nh_malloc(size_t size, int nhFlag)
{
 return HeapAlloc(GetProcessHeap(), 0, size);
}

size_t __cdecl _msize(void* pMem)
{
 return HeapSize(GetProcessHeap(), 0, pMem);
}

--- End code ---

Next one’s quite large!


--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
// FltToCh and FltToWch take as input char or wchar_t pointer p, which points to a buffer with
// adequate size to contain the resulting converted character string.
//
//   cl FltToCh.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include <windows.h>
#ifdef _M_IX86
   #include <emmintrin.h>
#endif
#include "malloc.h"
#include "string.h"
#include "stdlib.h"
#include "memory.h"
#include "stdio.h"


#ifdef _M_IX86
   unsigned int DoubleToU32(double x)
   {
    return (unsigned int)_mm_cvttsd_si32(_mm_set_sd(x));
   }
#endif


size_t __cdecl FltToCh(char* p, double x, size_t iFldWthTChrs, int iDecPlaces, char cDecimalSeperator, bool blnRightJustified)
{
 bool blnPositive,blnAbsValLessThanOne;
 bool blnRoundUpSuccessful=false;
 bool blnNeedToRoundUp=false;
 int iRoundingDigitLocation;
 size_t n,i=0,k=0;
 char* p1;
 
 p1=(char*)malloc(32);
 if(!p1)
    return 0;
 memset(p1,0,32);
 p1[0]=32, p1[1]=32;
 p1=p1+2;
 if(x>=0.0L)
    blnPositive=true;
 else
 {
    blnPositive=false;
    x=x*-1.0L;
 }
 if(x<1.0L)
    blnAbsValLessThanOne=true;
 else
    blnAbsValLessThanOne=false;
 #ifdef _M_IX86 
    n=DoubleToU32(x);
 #else
    n=(size_t)x;     
 #endif
 while(n>0)
 {
   x=x/10;
   #ifdef _M_IX86
      n=DoubleToU32(x);
   #else   
      n=(size_t)x;
   #endif
   i++;
 }
 *(p1+i) = cDecimalSeperator;
 x=x*10;
 #ifdef _M_IX86
    n=DoubleToU32(x);
 #else   
    n = (size_t)x;
 #endif 
 x = x-n;
 while(k<=17)
 {
   if(k == i)
      k++;
   *(p1+k)=48+(char)n;
   x=x*10;
   #ifdef _M_IX86
      n=DoubleToU32(x);
   #else   
      n = (size_t)x;
   #endif   
   x = x-n;
   k++;
 }
 *(p1+k) = '\0';
 iRoundingDigitLocation=(int)i+iDecPlaces+1;
 if(p1[iRoundingDigitLocation]>53)
    blnNeedToRoundUp=true;
 else
 {
    if(p1[iRoundingDigitLocation]==53  && p1[iRoundingDigitLocation-1] % 2)
       blnNeedToRoundUp=true;
 }
 p1[iRoundingDigitLocation]=0;
 if(blnNeedToRoundUp)
 {
    int iStart=iRoundingDigitLocation-1;
    for(int h=iStart; h>=0; h--)
    {
        if(h==i)
           continue;
        else
        {
           if(p1[h]!=57 && p1[h]!=cDecimalSeperator)
           {
              p1[h]=p1[h]+1;
              blnRoundUpSuccessful=true;
              break;
           }
           else
              p1[h]=48;
        }
    }
 }
 if(blnPositive)
 {
    if(blnRoundUpSuccessful==false && blnNeedToRoundUp==true)
    {
       p1[-1]=49;
       if(blnAbsValLessThanOne)
          blnAbsValLessThanOne=false;
    }   
    if(iDecPlaces==0)
       p1[i]='\0';     
    if(blnAbsValLessThanOne)
       p1[-1]=48;
 }
 else
 {
    if(blnRoundUpSuccessful==false && blnNeedToRoundUp==true)
    {
       p1[-1]=49;
       p1[-2]='-';
       if(blnAbsValLessThanOne)
          blnAbsValLessThanOne=false;
    }   
    if(iDecPlaces==0)
       p1[i]=0;     
    if(blnAbsValLessThanOne)
    {
       p1[-1]=48;
       p1[-2]='-';
    }
    else
    {
       if(p1[-1]==32)     
          p1[-1]='-';
    }       
 }
 
 int iSpaces=0;
 p1=p1-2;
 for(int i=0; i<18; i++)
 {
     if(p1[i]==32)
        iSpaces++;
 }
 for(int i=0; i<18; i++)
     p1[i]=p1[i+iSpaces];
 size_t iLen=strlen(p1);
 if(iLen>(iFldWthTChrs-1))
 {
   memset(p,'F',iFldWthTChrs-1);
    p[iFldWthTChrs-1]=0;
    free(p1);
    return 0;
 }   
 char* pField=(char*)malloc(iFldWthTChrs);
 if(!pField)
 {
    free(p1);
    return 0;
 }
 size_t iDiff = iFldWthTChrs - iLen -1;
 if(blnRightJustified)
 {
    for(size_t i=0; i<iDiff; i++)
        pField[i]=32;
    pField[iDiff]=0; 
    strcat(pField,p1);
 }
 else
 {
    strcpy(pField,p1);
   memset(pField+iLen,' ',iDiff);
    pField[iFldWthTChrs-1]=0;
 }
 strcpy(p,pField);
 free(p1);
 free(pField);
 
 return iFldWthTChrs-1;
}


size_t __cdecl FltToWch(wchar_t* p, double x, size_t iFldWthTChrs, int iDecPlaces, wchar_t cDecimalSeperator, bool blnRightJustified)
{
 bool blnPositive,blnAbsValLessThanOne;
 bool blnRoundUpSuccessful=false;
 bool blnNeedToRoundUp=false;
 int iRoundingDigitLocation;
 size_t n,i=0,k=0;
 wchar_t* p1;
 
 p1=(wchar_t*)malloc(64);
 if(!p1)
    return 0;
 memset(p1,0,64);
 p1[0]=32, p1[1]=32;
 p1=p1+2;
 if(x>=0.0L)
    blnPositive=true;
 else
 {
    blnPositive=false;
    x=x*-1.0L;
 }
 if(x<1.00000000000000L)
    blnAbsValLessThanOne=true;
 else
    blnAbsValLessThanOne=false;
 #ifdef _M_IX86 
    n=DoubleToU32(x);
 #else   
    n=(size_t)x;
 #endif 
 while(n>0)
 {
   x=x/10;
   #ifdef _M_IX86
      n=DoubleToU32(x);
   #else
      n=(size_t)x;
   #endif 
   i++;
 }
 *(p1+i) = cDecimalSeperator;
 x=x*10;
 #ifdef _M_IX86
    n=DoubleToU32(x);
 #else
    n = (size_t)x;
 #endif 
 x = x-n;
 while(k<=17)
 {
   if(k == i)
      k++;
   *(p1+k)=48+(wchar_t)n;
   x=x*10;
   #ifdef _M_IX86
      n=DoubleToU32(x);
   #else
      n = (size_t)x;
   #endif 
   x = x-n;
   k++;
 }
 *(p1+k) = L'\0';
 iRoundingDigitLocation=(int)i+iDecPlaces+1;
 if(p1[iRoundingDigitLocation]>53)
    blnNeedToRoundUp=true;
 else
 {
    if(p1[iRoundingDigitLocation]==53  && p1[iRoundingDigitLocation-1] % 2)
       blnNeedToRoundUp=true;
 }
 p1[iRoundingDigitLocation]=0;
 if(blnNeedToRoundUp)
 {
    int iStart=iRoundingDigitLocation-1;
    for(int h=iStart; h>=0; h--)
    {
        if(h==i)
           continue;
        else
        {
           if(p1[h]!=57 && p1[h]!=cDecimalSeperator)
           {
              p1[h]=p1[h]+1;
              blnRoundUpSuccessful=true;
              break;
           }
           else
              p1[h]=48;
        }
    }
 }
 if(blnPositive)
 {
    if(blnRoundUpSuccessful==false && blnNeedToRoundUp==true)
    {
       p1[-1]=49;
       if(blnAbsValLessThanOne)
          blnAbsValLessThanOne=false;
    }   
    if(iDecPlaces==0)
       p1[i]=L'\0';     
    if(blnAbsValLessThanOne)
       p1[-1]=48;
 }
 else
 {
    if(blnRoundUpSuccessful==false && blnNeedToRoundUp==true)
    {
       p1[-1]=49;
       p1[-2]=L'-';
       if(blnAbsValLessThanOne)
          blnAbsValLessThanOne=false;
    }   
    if(iDecPlaces==0)
       p1[i]=0;     
    if(blnAbsValLessThanOne)
    {
       p1[-1]=48;
       p1[-2]=L'-';
    }
    else
    {
       if(p1[-1]==32)     
          p1[-1]=L'-';
    }       
 }
 
 int iSpaces=0;
 p1=p1-2;
 for(int i=0; i<18; i++)
 {
     if(p1[i]==32)
        iSpaces++;
 }
 for(int i=0; i<18; i++)
     p1[i]=p1[i+iSpaces];
 size_t iLen=wcslen(p1);
 if(iLen>(iFldWthTChrs-1))
 {
   wmemset(p,L'F',iFldWthTChrs-1);
    p[iFldWthTChrs-1]=0;
    free(p1);
    return 0;
 }   
 wchar_t* pField=(wchar_t*)malloc(iFldWthTChrs*2);
 if(!pField)
 {
    free(p1);
    return 0;
 }
 size_t iDiff = iFldWthTChrs - iLen -1;
 if(blnRightJustified)
 {
    for(size_t i=0; i<iDiff; i++)
        pField[i]=32;
    pField[iDiff]=0; 
    wcscat(pField,p1);
 }
 else
 {
    wcscpy(pField,p1);
   wmemset(pField+iLen,L' ',iDiff);
   pField[iFldWthTChrs-1]=0;
 }
 wcscpy(p,pField);
 free(p1);
 free(pField);
 
 return iFldWthTChrs-1;
}

--- End code ---


--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//        cl atol.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"


long __cdecl atol(const char* pStr)
{
 char c,cNeg=NULL;           // c holds the char; cNeg holds the '-' sign.
 long lTotal=0;              // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr=='-')
 {
    cNeg='-';
    pStr++;
 }
 while(*pStr)
 {
    c=*pStr++;
    lTotal=10*lTotal+(c-48); // Add this digit to the total.
 }
 if(cNeg=='-')               // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}


int __cdecl atoi (const char* pStr)
{
 return ((int)atol(pStr));
}


long __cdecl _wtol(const wchar_t* pStr)
{
 wchar_t c,cNeg=NULL;        // c holds the char; cNeg holds the '-' sign.
 long lTotal=0;              // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr==L'-')
 {
    cNeg=L'-';
    pStr++;
 }
 while(*pStr)
 {
    c=*pStr++;
    lTotal=10*lTotal+(c-48); // Add this digit to the total.
 }
 if(cNeg==L'-')              // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}

--- End code ---



--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//       cl _atoi64.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"


_int64 __cdecl _atoi64(const char* pStr)
{
 char c,cNeg=NULL;           // c holds the char; cNeg holds the '-' sign.
 _int64 lTotal=0;            // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr=='-')
 {
    cNeg='-';
    pStr++;
 }
 while(*pStr)
 {
    c=*pStr++;
    lTotal=10*lTotal+(c-48); // Add this digit to the total.
 }
 if(cNeg=='-')               // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}


_int64 __cdecl _wtoi64(const wchar_t* pStr)
{
 wchar_t c,cNeg=NULL;        // c holds the char; cNeg holds the '-' sign.
 _int64 lTotal=0;            // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr==L'-')
 {
    cNeg=L'-';
    pStr++;
 }
 while(*pStr)
 {
    c=*pStr++;
    lTotal=10*lTotal+(c-48); // Add this digit to the total.
 }
 if(cNeg==L'-')              // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}

--- End code ---



--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//         cl abs.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"

int __cdecl abs(int n)
{
 if(n<0)
    return -n;
 else
    return n;
}
 
_int64 __cdecl _abs64(__int64 n)
{
 if(n<0)
    return -n;
 else
    return n;
}

long labs(long n)
{
 if(n<0)
    return -n;
 else
    return n;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
//#include "memory.h"   // Don't Forward Declare memcpy.  Gives Problems!

extern "C" void* __cdecl memcpy(void* pDest, void* pSrc, size_t iCount)
{
 char* pDestination=(char*)pDest;
 char* pSource=(char*)pSrc;

 for(size_t i=0; i<iCount; i++)
     pDestination[i]=pSource[i];

 return pDest;
}

--- End code ---

I did not develop the code below in win32_crt_math.cpp.  It was developed by Martins Mozeiko and posted at ….

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

It is only used in 32 bit architecture.  See _M_IX86 #define below.  Martins basically took the asm source from the …\crt\src subdirectories of VC and modified it so it would function within C++ naked functions, which are functions (if you can call them that) without prolog or epilog stack setup code.  These functions are only used by the compiler for various floating point math manipulations.

continued...

Navigation

[0] Message Index

[#] Next page

Go to full version