Guide - How to avoid C/C++ runtime on Windows

FYI: I've set this up with VS2015 and it looks like memset() doesn't need to be defined anymore. The BigArray works fine without it on a x64 build.

Thanks for the detailed guide, very handy.
Why would you want something like that instead of global new/delete operators? Defining them doesn't introduce dependency on libstdc++.
It would look like this: https://gist.github.com/mmozeiko/...25210cbe61bff49b6375762976acc866e

You'll need to override also array new[] and delete[]. See here:
http://en.cppreference.com/w/cpp/memory/new/operator_new
http://en.cppreference.com/w/cpp/memory/new/operator_delete

Edited by Mārtiņš Možeiko on
You don't need your approach at all.
Call to constructor and destructor will be generated automatically by compiler in my approach.

Try this (either run it and check the output, or step through with debugger): https://gist.github.com/mmozeiko/...3097cb3ba77370105fd5870c8a6c7367a
Thanks very much for this mmozeiko! I have been working on this since almost the beginning of 2016. Here is where I found out how to fix the...

_fltused

thingie!

Just yesterday I got stuck on the _dtoi3 unresolved external linker error, and am going to try to incorporate your code to fix that for x86 builds involving numbers.

What I am trying to accomplish is a complete application development system in C++ without anything from the C++ Standard Libraries, which I view as completely unrestrained bloatware. For many years I have used my own String Class, which compiles much smaller than the C++ Std. Lib. string class, but the sizes of my builds are still too large for me with the standard MSVC compiles. This system you have described here remedies that situation, and I have my String Class compiled down to the point where I'm getting 5 to 6 k x64 asci/Unicode exes.

My String Class is different from the C++ Std. Lib. String Class in that it also has some of the formatting functionality of iostream. That's where I ran into the unresolved external linker errors with _dtoi3 in attempting to get it to work in x86.

This whole business has been like pulling teeth! One problem after another. Your postings have really helped though! Thanks a lot!!!

Fred
I'm not aware of _dtoi3 function. msvcrt.lib MSVC C runtime library doesn't have such function. Are you sure it is _dtoi3 and not _dtol3? _dtol3 is generated when you cast double to int64. You need to implement yourself this function.

But if it is really _dtoi3 function, please show me the code that creates it - you can get assembly output by providing /Fa compiler switch for the .c/cpp file that produces call to this function.

Edited by Mārtiņš Možeiko on
It really is _dtoui3 Mmozeiko. I’ll see shortly about getting the asm output. Here’s the console output though showing the error…

1
2
3
TCLib.lib(FltToCh.obj) : 
error LNK2019: unresolved external symbol __dtoui3 referenced in function _FltToCh
Test1.exe : fatal error LNK1120: 1 unresolved externals


I’m using MS VC from Visual Studio 2015 (version 19 I believe).

In terms of either x86 or x64, its my understanding that a long and an int are basically the same thing. I mostly use Windows, but I’m guessing there must be other OSs where they are different.

Basically, for three months I’ve been back and forth between x86 and x64 code trying to get a system put together that will work for x86 /x64, wide / asci. Its just one problem after another, with no end in sight. And now this in x86 because of the numeric issue in handling 64 bit numbers, which, I might be able to see my way clear without if it will solve my problem, i.e., just limit x86 to dealing with 32 bit numbers. Haven’t decided yet on the issue. I’d like to see first if I can get this working with the code and techniques you’ve provided.

Basically, all my issues in trying to develop this have revolved around the fact that without msvcrt I have no way of converting floating point numbers to character strings. I originally started with Matt Pietrek’s (from Microsoft Systems Journal) LibCTiny.lib….

http://www.wheaty.net/

He originally did this work in the late 90s and updated it 2003 or so. He provided a rendition of printf and sprintf which worked, save for not being able to convert floating point numbers, which I consider to be a big problem. To solve this for x86 I found some asm code in Lib format at…

www.masm32.com

…written by Raymond Filiatreault. I got that to work for x86 with some inline asm, but I can’t call x86 libs from x64, obviously. I wanted a solution for both. As a quick hack I decided to do a Load Library / GetProcAddress on msvcrt.dll which did indeed solve my problem with printf/sprintf without adding anything to program size. However, that solution/hack wasn’t long term satisfactory to me. I wanted to free myself of both the C Runtime and anything in the C++ Standard Libraries. In that sense, doing a LoadLibrary/GetProcAddress is kind of cheating. So I wrote my own code to do the conversion from floating point to asci/wide character strings. It also does rounding, left/right justification in field, specification of number of places after decimal point, and accepts the character to be used for a decimal point, i.e., European countries tend to use the comma for a decimal point, I am told. Here is a standard C++ program using my FltToCh function just mentioned to convert 999.99999 to 1000.00 if two decimal places are specified…

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// cl Problem1.cpp
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <stdio.h>


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.00000000000000L)
    blnAbsValLessThanOne=true;
 else
    blnAbsValLessThanOne=false;
 n=(size_t)x;
 while(n>0)
 {
   x=x/10;
   n=(size_t)x;
   i++;
 }
 *(p1+i) = cDecimalSeperator;
 x=x*10;
 n = (size_t)x;
 x = x-n;
 while(k<=17)
 {
   if(k == i)
      k++;
   *(p1+k)=48+(char)n;
   x=x*10;
   n = (size_t)x;
   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;
}

int main()
{
 char szBuffer[16];
 double dblNumber;
 size_t iLen=0;
 
 memset(szBuffer, 0, 16);
 dblNumber=999.9999999;
 iLen=FltToCh(szBuffer, dblNumber, 16, 2, '.', false);
 printf("iLen     = %u\n",iLen);
 printf("szBuffer = %s\n",szBuffer);
 getchar();
 
 return 0;
}


And the compilation string and output is simply this with a program name of Problem1.cpp…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
C:\Code\VStudio\VC15\LibCTiny\x86\Test2>cl Problem1.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

Problem1.cpp
Microsoft (R) Incremental Linker Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Problem1.exe
Problem1.obj

C:\Code\VStudio\VC15\LibCTiny\x86\Test2>problem1
iLen     = 15
szBuffer = 1000.00


But when I compile my FltToCh and FltToWch into an obj file and add it to my Lib, then I get the error dtoui3 I previously posted.

In looking at the code and reading your reply above stating that _dtoul3 is used for converting doubles to 32 bit ints, I suspect the source of the call involves my variable ‘n’ above where there are several lines like so…

N = (size_t)x

Where ‘x’ is a double. Right now I’m in the process of studying your win32crt_math.cpp file and trying to incorporate it into my Lib to see if I can overcome yet this hurdle in this never ending battle to succeed at this. As far as I’m concerned I’ve finally got it completely beaten in x64. All that’s left now is x86. I just tested your win32_crt_math.cpp and it compiles fine. I added it to my library TCLib.lib, which contains the following *.obj files….

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
crt_con_a.obj          // start up code for console mode asci app
crt_con_w.obj          // start up code for console mode wide app
crt_win_a.obj          // start up code for windows gui asci app
crt_win_w.obj          // start up code for windows gui wide app
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           // my floating point to asci/Unicode characters
atol.obj
_atoi64.obj
abs.obj
win32_crt_math.obj    // molinkos code


I’m testing with this…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// cl Test1.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib               // doesn't work
// cl Test1.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib                                 // doesn't work
// cl Test1.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib FltToCh.obj kernel32.lib   // doesn't work
// 5,632 bytes with TCLib.lib  22 times smaller than with C Std. Lib.
//#define  UNICODE
//#define  _UNICODE
#include <windows.h>
#include "stdio.h"
#include "tchar.h"
extern "C" int _fltused=1;

int _tmain()
{
 TCHAR szBuffer[16];
 double dblNumber;
 
 dblNumber=999.9999999;
 FltToTChar(szBuffer, dblNumber, 12, 2, _T('.'),false);
 _tprintf(_T("%s\n"),szBuffer);
 getchar();

 return 0;
}


Here’s the command line compilation results showing the _dtoui3 linker error…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
C:\Code\VStudio\VC15\LibCTiny\x86\Test2>cl Test1.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

Test1.cpp
Microsoft (R) Incremental Linker Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test1.exe
TCLib.lib
kernel32.lib
Test1.obj
TCLib.lib(FltToCh.obj) : error LNK2019: unresolved external symbol __dtoui3 referenced in function _FltToCh
Test1.exe : fatal error LNK1120: 1 unresolved externals


Any thoughts? How would I go about creating a _dtoui function?
Just re-read your post mmozeiko. Its casting a double to int64 then. Well, its gotta be those lines I mentioned where I have this...

1
2
3
size_t n;
double x;
n=(size_t)x;


That's needed in my algorithm to seperate the digits of the input double so as to convert to asci/wide chars. Recommendations to beat this issue???
First of all, initially you said you are missing _dtoi3 function, not _dtoui3. _dtoi3 function doesn't exist 100% :)

But _dtoui3 function exists. As you discovered yourself, it is meant for converting double to unsigned int (or any type that is the same, for 32-bit Windows code that would be size_t, unsigned long and others).

You have two options:
1) Instead of using casts, you implement your own casting function.
So instead of
1
2
3
size_t n;
double x;
n=(size_t)x;

you would write:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
unsigned int DoubleToU32(double x)
{
    unsigned int r;
    ...
    return r;
}

size_t n;
double x;
n=DoubleToU32(x);

This is what Casey is doing in HandmadeHero. He is not implementing these functions yet, but that can be done later.

2) Second option would be to implement _dtoui3 function (and others, if you need):
1
unsigned int _dtoui3(double x) { ... }

Make function externally visible and implement in one file.

Now the question is how to implement it. There are several options here. First is just to take some opensource and copy/paste it. Here's llvm one (MIT licence): https://llvm.org/svn/llvm-project.../lib/builtins/fp_fixuint_impl.inc

But you can do better. If you know that range of double is limited, for example if double x is in [-2^31 .. 2^31) interval, you can do it with just one SSE2 instruction: _mm_cvtsd_si32. It is OK to use SSE2 instructions, because you are getting error about missing _dtoui3 function which is present only when you are compiling code with enabled SSE2 instruction set.

The conversion function will look like this:
1
2
3
4
unsigned int DoubleToU32(double x)
{
    return (unsigned int)_mm_cvtsd_si32(_mm_set_sd(x));
}


If you need to cover whole [0..2^32) range for unsigned int conversion, you can add a bit more code to get it to work. In pseudocode:
1
2
3
4
5
6
7
8
unsigned int DoubleToU32(double x)
{
    if (x >= double(2^31))
    {
        return 2^31 + _mm_cvtsd_si32(x - double(2^31));
    }
    return _mm_cvtsd_si32(x);
}

Of course for values outside of unsigned int range this will return garbage. You can add extra checks to return whatever value you want. This can be implemented completely branch free with SSE2 intrinsics - comparison will return mask and you can use it to select from two values - and + andnot + or instructions.

And I have comment about wide characters. Don't use it. They are bad. Their type is compiler specific, and on Windows they don't really do what they are supposed to do (cover all unicode characters) - so the surrogate pairs are introduced and all the advantage of fixed width unicode char type is lost. You cannot have random access to n-th string character and you cannot split string at any place. Doing so will lead to rendering bugs, or more serious security issues.

Just use UTF-8 and you'll be fine. Most of string functions will be exactly the same for ascii and utf-8 types. You can actually drop ascii and use just utf-8 strings, because utf-8 is superset of ascii, so all ascii strings automatically are valid utf-8 strings. If you need to interact with Windows wide char API, convert to wide char from utf-8 just before that call, and covert back to utf-8 immediately after the call. This will be much better way than using wchar_t type.

If utf-8 is really really unacceptable (why?), then at least use the char32_t type. It is C++11 type, but it covers all possible unicode char range.

Edited by Mārtiņš Možeiko on
mmozeiko
Just use UTF-8 and you'll be fine. Most of string functions will be exactly the same for ascii and utf-8 types. You can actually drop ascii and use just utf-8 strings, because utf-8 is superset of ascii, so all ascii strings automatically are valid utf-8 strings. If you need to interact with Windows wide char API, convert to wide char from utf-8 just before that call, and covert back to utf-8 immediately after the call. This will be much better way than using wchar_t type.

If utf-8 is really really unacceptable (why?), then at least use the char32_t type. It is C++11 type, but it covers all possible unicode char range.


Windows usually has both utf8 and utf16 versions of the string-receiving functions you can usually just append A to ensure you always use the UTF8 version.
That is not correct.

A functions stands for ANSI, and they do not support utf-8 encoding. Any byte in string with value >= 128 (part of utf-8 multibyte char) will be interpreted in system specific locale. Meaning it will be different characters for different configuration of Windows. https://msdn.microsoft.com/en-us/...ary/windows/desktop/dd317752.aspx
Characters represented by the remaining codes, 0x80 through 0xff, vary among character sets. Each character set includes different special characters, typically customized for a language or group of languages. Windows code page 1252 and OEM code page 437 are generally used in the United States.

W functions depends on Windows version. For older Windows that 2000 it interprets strings in UCS-2 encoding. For Windows 2000 and up it is UTF-16. Of course, nobody now cares about Windows version older than 2000, so it is UTF-16 pretty much everywhere.
In terms of either x86 or x64, its my understanding that a long and an int are basically the same thing. I mostly use Windows, but I’m guessing there must be other OSs where they are different.

Even if you're only focusing on Intel CPUs, the assumption that int and long are one and the same fails as soon as you port to Linux and Mac OSX.

On Linux and OSX an int will be 32-bit only and a long will be 64-bit. (LP64 model) while Windows chose to use 32-bit int and long (LLP64 model)

You can see below at this Wikipedia link the table of the various data models chosen for various platforms:
https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models

Even if you're only focusing on Intel CPUs, the assumption that int and long are one and the same fails as soon as you port to Linux and Mac OSX.

On Linux and OSX an int will be 32-bit only and a long will be 64-bit. (LP64 model) while Windows chose to use 32-bit int and long (LLP64 model)

I assumed that was likely the case uucidl. Thanks for the clarification. It always seemed to me Microsoft was wasting an intrinsic C based data type by defining ints and longs as basically the same thing.

And I don't want you to think I'm ignoring your valuable suggestions mmozeiko. I truly appreciate the help you've given. Its just that I've been trying to implement your suggestions and studying things in general. I'll get back with a more detailed reply in a bit. Yep, still working on it!
By #including <emmintrin.h> I was able to get this idea/suggestion of yours working Mmozeiko…

1
2
3
4
unsigned int DoubleToU32(double x)
{
 return (unsigned int)_mm_cvtsd_si32(_mm_set_sd(x));
} 


…in that, my program including my FltToCh() function would now compile, link, and run, whereas before it wouldn’t. That’s the good news. The bad news is that it broke my algorithm which worked perfectly in 64 bit. I attempted to fix the worst of it and had some measure of success. But what stopped me was that my algorithm lost precision to the point where I was only getting a few digits of accuracy. At that point something snapped in my brain and I decided I’d had it with this.

I’ve often heard it said that a good general picks his battles and refrains from fighting battles he can’t win. My situation here is something like that, but not exactly. For you see, this is a battle I already won about six weeks ago. I see no reason to re-fight it. I already have a solution to the x86 issue of converting floating point values to character strings. That would be my use of Raymond Filiatreault’s fpu.lib written in masm…

http://www.website.masmforum.com/tutorials/fptute/

I had hoped to code a solution that would work in both x86 and x64, but I’ve given up attempting to achieve it for the reasons which you’ve pointed out to me. If I really absolutely need to have my code work in both x86 and x64 I can use my already working x86 fpu.lib solution for x86 and my FltToCh() function I previously posted in this thread for x64. In examining my original priorities, goals, and objectives for this project I never really considered it essential that the code be essentially the same for x86 verses x64. In other words, do what needs done for each with the bottom line being simply that it works.

But my priorities have always been 64 bit with wide character support. Ansi was always somewhat less important to me as was x86 less important. And I have succeeded beyond my wildest expectations. To illustrate, lets take this C++ program to parse a CSV string such as this…

1
"Zero, One, Two, Three, Four, Five, Six";


Not too hard. Here is a short program to do it with the output afterwards. I’m using VC 19 from Visual Studio 2015…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// cl StdLibParse.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


Right after the command line compilation string above which as you can see optimizes for size with an /MT stand alone executable release build we end up with a 200,192 byte bloated binary. Now let me show you my definition of success. We’ll start here with my TCLib.mak file which can be run with nmake.exe..

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//           TCLib.mak
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
        
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) $<


All the *.cpp and *.h files are in the attached zip. You can recreate this if you care to. It needs to be run through the x64 compiler. It should build TCLib.lib (Tiny C Library). Having done that here is my version of the above C++ program that parses that string. Note I’ve put an abbreviated version of my String Class inline above main(). It has just enough members to do the job. My full String Class is closer to 900 lines long. Here’s the code with command line compilation string at top. Its named Parse.cpp…

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// cl Parse.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
// 3,584 Bytes
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"

class String
{
 public:
 String()   // Uninitialized Constructor
 {
  this->lpBuffer    = new char[16];
  this->lpBuffer[0] = 0;
  this->iLen        = 0;
  this->iCapacity   = 15;
 }
  
 String(const char* pStr)  //Constructor: Initializes with char*
 {
  this->iLen=strlen(pStr);
  int iNewSize=(this->iLen/16+1)*16;
  this->lpBuffer=new char[iNewSize];
  this->iCapacity=iNewSize-1;
  strcpy(lpBuffer,pStr);
 }

 String& operator=(const char* pStr)  // Assign char* To String
 {
  size_t iNewLen=strlen(pStr);
  if(iNewLen>this->iCapacity)
  {
     delete [] this->lpBuffer;
     int iNewSize=(iNewLen*2/16+1)*16;
     this->lpBuffer=new char[iNewSize];
     this->iCapacity=iNewSize-1;
  }
  strcpy(this->lpBuffer,pStr);
  this->iLen=iNewLen;
    
  return *this;
 }
  
 int ParseCount(const wchar_t c)   //returns one more than # of
 {                                 //delimiters so it accurately
  int iCtr=0;                      //reflects # of strings delimited
  char* p;                         //by delimiter.

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

  return ++iCtr;
 }
 
 void Parse(String* pStr, char delimiter, size_t iParseCount)
 {
  char* pBuffer=new char[this->iLen+1];  
  if(pBuffer)
  {
     char* p=pBuffer;
     char* 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;
  }
 }
   
 char* lpStr()
 {
  return this->lpBuffer;
 } 
 
 void Print(bool blnCrLf)
 {
  printf("%s",lpBuffer);
  if(blnCrLf)
     printf("\n");
 }
   
 ~String()
 {
  delete [] this->lpBuffer;
 } 
  
 private:
 char*  lpBuffer;
 size_t iLen;
 size_t iCapacity; 
};


int main()
{
 String s1       = "Zero, One, Two, Three, Four, Five, Six";  // Assign CSV String To Be Parsed
 int iParseCount = s1.ParseCount(',');                        // Determine Number Of CSVs
 String* pStrs   = new String[iParseCount];                   // Allocate Array To Hold Above Determined Number of CSVs
 s1.Parse(pStrs, ',', iParseCount);                           // Parse The String
 for(int i=0; i<iParseCount; i++)                             // Output The CSVs
     pStrs[i].Print(true);                                    // ....
 delete [] pStrs;                                             // De-Allocate Dynamic Memory
 
 return 0;
}


So there you have my definition of success, i.e., an x64 C++ program containing a String Class which compiles as a stand alone executable to 3,584 bytes, which, if you do the division, comes in at 55.8 times smaller than the 200,192 byte Standard Library based program first shown! Here’s the results of the command line compilation with program run output…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
C:\Code\VStudio\VC15\LibCTiny\x64\Test14>cl Parse.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Parse.cpp
Microsoft (R) Incremental Linker Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Parse.exe
TCLib.lib
kernel32.lib
Parse.obj

C:\Code\VStudio\VC15\LibCTiny\x64\Test14>Parse
Zero
 One
 Two
 Three
 Four
 Five
 Six

C:\Code\VStudio\VC15\LibCTiny\x64\Test14>


Exactly the same output to the last space as the C++ Standard Bloatware Library version. The logic and structure of my solution above, as well as my String Class, is based upon a PowerBASIC version of this program, which looks like this…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#Compile Exe                                          ‘Create Stand Alone Exe as opposed to Dll or Lib
#Dim All                                              ‘Require All Variables To Be Declared

Function PBMain() As Long
  Local iParseCount As Long                           ‘To Contain Number Of CSVs
  Local strLine As Wstring                            ‘A OLE String Engine BSTR To Hold CSVs
  Local pStrs() As Wstring                            ‘A Dynamic Array Of COM BSTRs
  Register i As Long                                  ‘Use Register For Iterator

  strLine="Zero, One, Two, Three, Four, Five, Six"    ‘BSTR Containing CSVs
  iParseCount=ParseCount(strLine,",")                 ‘ParseCount() Returns # of CSVs
  Redim pStrs(iParseCount) As Wstring                 ‘Dynamically Allocate iParseCount # of BSTRs
  Parse strLine, pStrs(), ","                         ‘Parse CSVs Based On ‘,’; Put CSVs Into pStrs()’s Array
  For i=0 To UBound(pStrs, 1) –1                      ‘Iterate Through Array pStrs()
    Console.Print LTrim$(pStrs(i))                    ‘Output Trimmed Strings To Console
  Next i
  Erase pStrs()                                       ‘Release Dynamically Acquired Memory
  Waitkey$

  PBMain=0
End Function

#if 0
Zero
One
Two
Three
Four
Five
Six
#EndIf


The above 32 bit PowerBASIC program compiles to 15,360 bytes. If you compare it to my C++ program above you’ll immediately see the resemblance. However, its hard to compare these programs in terms of size because of the radically different nature of the C++ language and the PowerBASIC language. The PowerBASIC program above has already initialized the COM subsystem of Windows and and has an OLE based String variable type built right into the language – unlike C/C++. A lot of other things going on too that I won’t get into here. But the reason I’m doing this work here in attempting to develop a C++ application development framework which eliminates the C and C++ Standard Libraries has a lot to do with PowerBASIC. For you see, the world class programmer who developed the PowerBASIC programming language – Robert Zale, passed away several years ago, while development of a 64 bit compiler was under way. He never completely finished it. The company still exists in skeleton form, but its doubtful whether it will be able to finish Bob’s 64 bit compiler. So I needed to move on.

For the past 15 years I’ve used C and C++ for my embedded development work with Windows CE, as I develop our handheld data recorder programs. For mission critical desktop applications where I work in the forestry sector I’ve used PowerBASIC because of the extremely high performance (it’ll match C tick for tick as its really an extension of MASM using the exact same variable types, free use of inline assembler, etc) and more highly developed dynamic multi-dimensional array handling capabilities, much better string handling than C++, etc., etc. But, as I said, I fear there will never be any 64 bit version, nor any further development of the PowerBASIC language. So that leaves me with C and C++. Which will live on after Dennis Ritchie and Bjarne Stroustrup are gone, the former of whom has already departed us. C’s good and I’m reasonably good at it, but its very slow to develop with. I personally feel I need the enhanced capabilities of C++. Its just that I can’t live with the C++ bloat on desktop Windows (that doesn’t exist in Windows CE – that’s very lean), and the present propensities of the C++ anointed and acolytes to abstract everything, make classes out of everything, write 100 or a 1000 lines of code when something could be done elegantly with ten lines of code – drives me nuts (or even better yet, add a whole library to add 2 + 2 together)….

http://lispian.net/2011/11/01/lasagna-code/


Lasagna Code
November 1, 2011
By lispian
Anyone who claims to be even remotely versed in computer science knows what “spaghetti code” is. That type of code still sadly exists. But today we also have, for lack of a better term — and sticking to the pasta metaphor — “lasagna code”.
Lasagna Code is layer upon layer of abstractions, objects and other meaningless misdirections that result in bloated, hard to maintain code all in the name of “clarity”. It drives me nuts to see how badly some code today is. And then you come across how small Turbo Pascal v3 was, and after comprehending it was a full-blown Pascal compiler, one wonders why applications and compilers today are all so massive.
Turbo Pascal v3 was less than 40k. That’s right, 40 thousand bytes. Try to get anything useful today in that small a footprint. Most people can’t even compile “Hello World” in less than a few megabytes courtesy of our object-oriented obsessed programming styles which seem to demand “lines of code” over clarity and “abstractions and objects” over simplicity and elegance.
Back when I was starting out in computer science I thought by today we’d be writing a few lines of code to accomplish much. Instead, we write hundreds of thousands of lines of code to accomplish little. It’s so sad it’s enough to make one cry, or just throw your hands in the air in disgust and walk away.
There are bright spots. There are people out there that code small and beautifully. But they’re becoming rarer, especially when someone who seemed to have thrived on writing elegant, small, beautiful code recently passed away. Dennis Ritchie understood you could write small programs that did a lot. He comprehended that the algorithm is at the core of what you’re trying to accomplish. Create something beautiful and well thought out and people will examine it forever, such as Thompson’s version of Regular Expressions!
Maybe it’s just my age and curmudgeonly nature shining through, but it pains me to write code for many systems. It’s just so ugly, so poorly thought out. There are bright spots, but they’re rarer by the year. No wonder so many kids decide not to go into computer science. Where it was once applied mathematics with all its intrinsic beauty it’s now been reduced to slapping at the keyboard, entering thousands of lines hoping the compiler will allow your code to compile. Where’s the elegance that was Lisp or Smalltalk or APL? Hell, even Fortran was more elegant than a lot of the crap programming languages being touted today. Why hasn’t someone gone back to Algol and pushed that forward.
As I mentioned to my kids the other day, it’s sad when one of the best programming languages remains C. Sure, there are some beautiful small languages out there that do niche work, but mainstream? Nothing. It’s just a catastrophe. Something like Python may have been great if they’d not embedded an object model into its guts. Sigh.

And I’ve never liked anything in the C++ Standard Library. And I’m independent minded enough to write my own library code which believe it or not largely works. So in a nut shell that’s basically where I’m coming from with this code I’ve developed and posted here. I decided to post it here because you’ve really helped me big time Mmozeiko. I was stuck on that _fltused thing for days trying to get to the bottom of what was going on with floating points when the C Runtime was eliminated. I should have done an internet search on it sooner rather than wasting days trying to figure it out myself. For when I did I found this site and your post here within about ten minutes. And I saw a lot of folks really appreciated the information you provided, and it amazed me to see that there were other folks other than I who were bothered by the ridiculous bloat produced by Microsoft’s C/C++ compilers. Its been my experience that almost nobody cares about this. I’ve been told a million times about the fact that hard drives and ram are now virtually infinite in size, with processors being almost infinitely fast, so efficiency and conciseness in coding no longer has any merit. But nothing seems to run any faster, as Gate’s Law effectively nullifies Moore’s law, being as while processor speeds double every 18 months, software speeds half themselves in that same time span….

http://catb.org/jargon/html/G/Gatess-Law.html

Gates's Law

“The speed of software halves every 18 months.” This oft-cited law is an ironic comment on the tendency of software bloat to outpace the every-18-month doubling in hardware capacity per dollar predicted by Moore's Law. The reference is to Bill Gates; Microsoft is widely considered among the worst if not the worst of the perpetrators of bloat.

…no doubt related to the very issues under discussion here, i.e., software application framework bloat.

By the way, by eliminating the MSVC Runtime, an x64 GUI program created through RegisterClassEX() and CreateWindowEx() comes in also at an amazing 3 k!!! Can you imagine that? Here would be that…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// cl Form1.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib
// cl Form1.cpp /O1 /Os /GS- /link kernel32.lib user32.lib gdi32.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;
}


You can see the stats above at top. 3,072 bytes with my TCLib.lib, and 84,992 bytes using the standard MS VC19 build as an /O1 /Os /MT Release build. As perhaps an interesting aside, here is what the above GUI looks like in PowerBASIC. It compiles to 6,656 bytes with PowerBASIC…

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
'PowerBASIC Version Form1  Disk Image 6656 bytes;  size on disk 8192; Windows Explorer 7K
#Compile Exe
#Dim     All
#Include Once "Win32Api.inc"


Function fnWndProc(ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  If msg=%WM_DESTROY Then
     Call PostQuitMessage(0)
     fnWndProc=0 : Exit Function
  End If
  fnWndProc=DefWindowProc(hWnd, msg, wParam, lParam)
End Function


Function WinMain(ByVal hInstance As Long, ByVal hPrevIns As Long, ByVal lpszArgument As Asciiz Ptr, ByVal iShow As Long) As Long
  Local szClassName As Asciiz*8
  Local wc As WNDCLASSEX
  Local Msg As tagMsg
  Local hWnd As Dword

  szClassName             = "Form1"
  wc.lpszClassName        = VarPtr(szClassName)
  wc.lpfnWndProc          = CodePtr(fnWndProc)
  wc.cbSize               = SizeOf(wc)
  wc.hbrBackground        = %COLOR_BTNSHADOW
  wc.hInstance            = hInstance
  RegisterClassEx(wc)
  hWnd=CreateWindowEx(0,szClassName,szClassName,%WS_OVERLAPPEDWINDOW,200,175,320,300,%HWND_DESKTOP,0,hInstance,ByVal 0)
  ShowWindow(hWnd,iShow)
  While GetMessage(Msg,%NULL,0,0)
    TranslateMessage(Msg)
    DispatchMessage(Msg)
  Wend

  WinMain=msg.wParam
End Function


So I hope you don’t mind my posting some of my code here. Delete it if you think its no good or out of place. I’d welcome any comments on it.

In closing I have to say that I don’t know what to make of your criticisims of the wchar_t variable type and Microsoft’s use of it. That you are a more knowledgible coder than I is without question so it would seem to behoove me to delve deeper into the matter with an eye to implementing your suggestions. But on the other hand I have to say that I’ve been using the wchar_t data type in all its bizarre manifestations (TCHARs, OLECHARS, etc., etc.) for 15 years and have encountered nothing of what you have described. And world renouned programmer/author Charles Petzold – writer of the famous “Programming Windows” series of books, has bestowed his full blessing upon it. So I can’t really reconcile these facts. I will look deeper into the issue when I get time. Thank you for the ‘heads up’ about it. I will not ignore your recommendation.

Edited by Fred Harris on Reason: fix formatting
And here's a zip file with all the lib code if the site will take it....