Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Wednesday, 25 June 2025

XVE Game Engine : Ecs Containers & Update

This post title might look mightily like the post I made in January it is related, but we're going to discuss a different part of my Entity Component System and that is the containers backing my type registration and the actual values stored per component.

I've seen a few different entity component systems and I'll draw parallels with a few well known public domain ones.

The first is EnTT, it uses the same principles as my system which are sparse sets to store the metadata in regards the components per entity, my implementation also uses sparse sets however inspecting their code repository file names alone you can see maybe three container types a dense set, a dense map and a table.

I have known maps used to point to vectors of stored components such that the entity handle or id is the index into the components it contains, and then all the other vector entries are nulls (or otherwise empty).

And then in yet other systems I have seen a fixed number of components (64) possible being represented by binary flagging or bitsets to indicate the presence (or absence) of a component on an entity, then a linear storage of each component in a fixef size array.  This was a curious implementation, relying on Frozen to give constexpr & consteval Ecs ability to a certain extent at compile time.

My implementation started with the need to map the types (just any type) and then to allow those types to assigned to the entities, very much the sparse set model of EnTT and its cousins out there.

Why not just use EnTT?  Well, where's the fun and understanding built out of that?

So where did I begin?  Well, I began by just using a std::map, std::unordered_map and std::vector and a piece of hashing code to convert the type to a compile time evaluated id code; so I can for any type get a 64bit number which is its unique (or nearly unique) hash.  I am not using RTTI and infact compile with this disabled for speed.

With my types mapped and components going into the entities and being able to see which types were assigned to an entity (see January's post) I set about looking at more specialised containers; and finally we reach the meat of this post... Or should I say Paper?

As my self-review, redesign and frank improvements began by the careful selection of the AVL Tree as my container of choice, a self balacing binary search tree, often lumped in with Red-Black trees.  I had spent a little time in 2019 invaluating a red-black tree implementation for work for an ex-explorer, and watched on as a collegue did the same evaluation for my current.  I had not been best impressed with the lob-sided effects in the former, and the latter went with an off the shelf implementation from EASTL.

 

 My reading began with a few white papers, a couple of older text books, the STL guide and finally a read through the second edition of Grokking Algorithms by Aditya Bhargava.

This final read cemented the AVL Tree as my target container, and I set about implementation from the books examples.

My first addition was to include a visualization pass outputting .DOT format diagrams for passing through GraphViz... And so it was the first output, inserting an "A" into a new tree appeared:


 

I find these tree diagrams most full-filling, and had to research the trick to lay the diagrams out as an actual tree like this, but soon I had test cases galore.


Inserting a second item and then a third, where we see the tree rebalance itself:

 

This rebalancing is the strength, I can take all the inserts of new types at the start up and then balance once; cost once, to get much faster look up than hashing every time and traversion a tree.


 
If you can't spot the re-balancing in the leaves, just watch for the root changing, once it does take all the left arms to find the lowest level index (zero) containing 'A' and then march parent, right, common parent and so on and so forth.

My code to flatten and allow this traveral is not very optimal, it can only really allow you to pre-allocate the correct number of keys to then follow.  But again if the structure is not changing often (which is it now with type registrations) then it becomes the most optimal runtime solution I found.

Only the Frozen in compile time version, with its inherant more complex compile & lack of debuggabilty was faster.  But then anything you can pre-compute is going to be faster.

I did however find an edge case, depending on the order of insertions, and hence when rebalacing happens, the same data can result in variants on the shape of the tree.  Take the name of my wife and some of my pets....

Compare this with the same data inserted in a different order:

These two trees contain the exact same data and same key set; but they are different in form.

I had to decide what constituted equality, this is where I resorted to a left-right linear traversal per node and this results in an equiality check; it's not quick, and not really intended for production code to use, it is simply part of my test suite.

This difference, or cardinality one might say, is a temporary effect on the data, and I wondered whether it would have a knock on effect for determinism in my code.

As you may guess, one tree in traversal (key) order and then reloading could dramatically change the order of the tree node relationships, as such I could not rely on the direct pointer (or dereference) to any node over another, no instead the key value was literally key.

I found this effect of saving and reloading to "rebalance" the tree had the benefit when documenting my registered entity types that having gone through this save & load the graphs output where the same, I termed this a sanitzation pass on some of my data and I'm sure I'll return to it.

The last feature I added to the AVL tree implementation was the removal of nodes, this may sound trivial, and to an extent it is.

However, I had programmed the use of my AVL Tree with "forward intent" this is a rather short sighted way of working that you produce code only to do the exact features you need, you either ignore, or ticket and backlog the "nice to haves".

So it was after a fortnight of working on the Containers, the speed, the hashing within my engine, not to mention the test suite & graph viz pass I had dozens of types registrations, thousands (probably hundreds of thousands of each entity type being created)... And then I needed to destroy an entity.

Quite simply it never came up, I'd forgotten it as a feature and test case, because sometimes when you're working at near midnight tinkering with tech, you do forget things.

So there you go, my little sojourn into AVL Trees for my home engine Ecs implementation, and I learned a lot along the way in just this one little case of my own making.

Anyone out there looking to take up programming, do it, just start simple and move up along the way.  I'd say start simple, with a language which can just run on any box, without too much hassle, just a text editor and a language to use; like python.  Progress in small steps.


Monday, 15 June 2020

The Mystery FTP Clocking Machine Project

During my very first programming job I was given, late in the day, a unit which gathered user data.  It stored this data in CSV plain text and according to the manual you were to FTP into the unit and retrieve the file.

I had an FTP client, I had an FTP implementation (in Dephi) and neither could connect to the device, standard FTP commands didn't work, they just didn't work.

We could confirm it had an IP Address, see the device within the DHCP list and indeed we could ping it, so it was responding to ICMP, but no matter what the FTP client, as described in their manual, would not connect.

A TFTP client similarly could not.

We had no internet, so after going home that evening, on my own initiative I downloaded three more FTP clients and even downloaded an FTP class for the C++ IDE we could use "Borland C++ for Windows".

In the morning I tried all of these, nothing, nada, naught.

I went to get another from the pile of 30 of these we had sat with customers waiting, nada.

I plugged away at this for a week and in the end arranged for an engineer from the vendor to come see us; the chap came, he had a peek and a poke at my code, saw nothing wrong, tried this test routines, they all ran... so we were at an impasse.

Nothing we tried worked except their test routines, so I of course wanted the code to their test routines.

They were very reticent to deliver it but after a lot of prodding and some negotiation over lunch it was agreed that their own engineer could receive the code, look at it on my machine, even copy and paste a few critical parts and get us up and running with connect, disconnect and that would be it, we could then have to do all the other commands, but their engineer would sit there whilst I did at least "list".

No internet, so I took this engineer to my home address (luckily within walking distance of the office) and over my modem (yes, I'm that old) we downloaded this code to a floppy disk (yes, I'm truly that old).

We headed back to the office, set up and opened the code next to my IDE window, their code was in C, so I would have to transliterate it into Delphi later, but whilst he was present we stuck with the C code, he called his programmer and the chap confirmed it should build in Borland C for DOS.  Which I had.

Sure enough their code seemed to compile fine and it ran in debug and connected, did a list and pulled all the files off the device, deleting them after.

Their engineer did this, then he looked at the code, and he smiled, and stared at the code, then smiled at me.  Remember we've wasted my time, his time, like a week in total here.

He consulted their own operators manual, looked at the bottom of the device, smiled again, then picked up the phone.


"Is there another version of this model?"

<squiggly reply on 1998 Motorola Razor>

"Sure, sure, but is there another version?"

<Squiggly voice>

"Right, right, can you send that?"

<Loud squiggly voice>

"Yes"

<Squiggly no>

"Yes"

And he hung up.

"Sorry gentlemen, he was addressing me and my boss, but ah... it would seem you've got the wrong device".

And he arranged for them all to be collected and returned without explaining himself, he personally returned with another unit, which to me looked identical and this worked instantly.

I have never found out quite what the issue was, but the next morning 29 more of these boxes arrived and we could deliver to our downstream clients the next week.

Friday, 15 February 2019

Coding Standards - About Scopes (again)

Consistency is good, consistency is light, consistency is really what you need think about when creating code, no matter what format, language, platform or project you're on do things the same way, and even on your first pass do them your way consistently before you apply any locally demanded view of the code....

What do I mean by view?  Well, I'm talking about coding standards, but the non-functional side of things, hence the view.  You can look at code from any angle you choose, indeed there are multiple ways to achieve the same solution within code, the difficulty is to get things out there, put them down, then sort the wheat from the chaff.

I've done this in a couple of videos, where I move pieces of code from their own coding layout to my own, I have labelled these "coding standards", but in retrospect they've just the coding view.

One of my big points is the notation for the scope of variables, I love to use scoped notation.  Having been brought into programming when Hungarian notation was not only taught to you academically as required, but demanded by all projects you worked on (basically as you had no code completion tools, so knowing the type of a named variable whenever you utilised it was really very useful) but that was twenty plus years ago, now we have code completion I strongly feel your tools should be made to work for you.

Therefore, when I complete a call or variable name, I want that to communicate something to me, and the most useful is to check the scope, the code completion tells me the type and the name, but scoping isn't really fully covered in the tooling (please post below any suggestions for Visual Studio, Atom, CodeBlocks or Visual Studio Code which perform scope notation or checking on the fly).

When I complete my typing and pause I want to see all the m_ members together, all the c_ constants all the s_ statics and so many coding standards documents define these three.  However, it's just not far enough, what happens when you have locals named 'angle', 'view' and 'Gregorian'?  They're at very different points within the code completion window which pops up, you have to remove your hands from the keyboard to begin scrolling through them; or worse still start typing letters in order to guess the name (for instance we may start to type a c to see if 'Gregorian' comes up actually as 'calendar').

My solution?  The solution I've been pushing time after time, post after post, video after video?  Scope more things.

We can add "local" as "l_", parameters as "p_", we can compound them, so a static constant maybe "sc_" and we can tailor this in whichever manner we feel fits best, but be consistent.

Let me be utterly clear, this is not Hungarian notation of old, we are not adding "i" to integers or "l" to longs, we are not defining the type within the name, we are simply defining the scope and helping the tools to help us by grouping the various scopes of variables together.

I find it incredibly hard to read documents which go half way, defining members as "m_" (or just a leading m) is extremely common, ditto for constants and even statics, it's only with parameters people get a little squeamish.  "p_" is sometimes reserved for pointer, but that's a style edging back to Hungarian you're notifying the reader of the type and use rather than the scope, therefore I push "p_" as a parameter.  But I can also see arguments for "i_" and "o_" for parameters, which tell you it's an input or output to the function.  If one wanted to be a pedant you could go so far as to stress this with "pi_" and "pi_".

This isn't to say I'm for everything, for example in returning from a function:

int foo();

I will not define an "r_" as the return scope, I'm not interested in the semantics to that extent, and I don't feel code like this:

int foo()
{
int r_result(0);

... Some code to set r_result

return r_result;
}

Is communicating anything useful, so often in my code you will find:

int foo()
{
int l_result(0);

... Some code to set l_result

return l_result;
}

As the result remains a local until returned.

Which brings me back to my second bug bear of the month, one which I've already blogged about returning early.  I'll let you remind yourselves of my opinions.




And just a note to the world at large, if you're coding "m_" for members, but ALSO add Hungarian notations "m_iInteger" stop, stop it now... and yes that includes when you're doing pointers "int* m_pIntegerList"... argh, I want to pull my own arm off just to have something to throw at the screen when I see this one, it's a hybrid as annoying as the Alien in Alien Resurrection.


What's my solution?... Well the completely sane idea I use is "m_IntegerListPtr"... Yes, it's a pointer... I tell myself it's one, I'm not strange at all... Alright, maybe a little.


Thursday, 31 January 2019

C++ Coding Standards: Don't Return Early

I've just had one of those mini-conversations which has be scratching my head, I asked a coworker about "return early" being part of the coding standard or not, as it was being discussed by other parties.

His response was an emphatic, slightly arrogant and touchily obtuse, "I have no time for anyone arguing for not returning early from a function you've discovered you should not be in".

I however don't take such a black and white view, I see the pro's and con's of both approaches, importantly not returning from a function can, and is part of, driving process flow conversations and it aids in picking out structure which can be encapsulated away.  The result being that instead of instantly exiting and potentially leaving spaghetti code you can see a flow, see where branching is occurring and deal with it...

But, that said, it must be understood "returning early" is often seen as "faster", as the chap argued "I see no point being in a function you know you should already have left", so I took to compiler explorer... In a very trivial example:

 

Here we see a decision being made and either a return or the active code, easy to read, simple, trivial... But don't dismiss it too early, is it the neatest code?

Well, we could go for less lines of code which generates near identical assembly thus:


This is certainly less lines of C++ however, inverting the test is a little messy and could easily be overlooked in our code or in review, better might therefore be expanding the test to a true opposite:


One could argue the code is a little neater to read without the inverting, critically though it has made no difference to the assembly output, it's identical.

And it is identical in all three cases.

You could argue therefore that "returning early has no bearing on the functionality of the code", but that's too simplistic, because "not returning early" also has no bearing on the functionality of the code.  The test and action and operation has been worked out by the magic of the compiler for us to be the same.

So with equivalent operation and generation we need think about what returning from the function early did affect, well it made the code on the left longer, yes this is an affectation of our coding with braces, but it was longer.  You could also see that there were two things to read and take in, the test was separate to the code and importantly for me the test and the actual functional code were on the same indentation horizontal alignment.  Being on that same alignment makes your eye not think a decision has been made.

Putting the test and the action of that test into the inner bracing communicates clearly that a decision has been made and our code has branched.

And when that structure is apparent you can think about what not returning early has done, well it's literally braced around the stanza of code to run after the decision, that packet of code could be spun off into it's own function do you start to think about encapsulation.  Of course you can think about the same thing after the return, but in my opinion having the content of the braces to work within is of benefit and most certainly does not afford any speed benefits.

Lets look at a more convoluted, but no less trivial example, a function to load a file in its entirety and return it as a known sized buffer... we'll start with a few common types:

#include <memory>
#include <cstdint>
#include <string>
#include <cstdio>
#include <optional>
#include <sys/stat.h>
#include <sys/types.h>
using byte = unsigned char;
using DataBlock = std::shared_ptr<byte>;
using Buffer = std::pair<uint32_t, DataBlock>;
using LoadResult = std::optional<Buffer>;

And we'll assume there's a function to get the file size, with stat...

const uint32_t GetFileSize(const std::string& p_Path)
{
    uint32_t l_result(0);

    struct stat l_FileStat;
    if ( stat(p_Path.c_str(), &l_FileStat) == 0)
    {
        l_result = l_FileStat.st_size;
    }

    return l_result;
}

Now, this file is return path safe because I define the result as zero and always get to that return, I could have written it thus:

const uint32_t GetFileSizeOther(const std::string& p_Path)
{    
    struct stat l_FileStat;
    if ( stat(p_Path.c_str(), &l_FileStat) != 0)
    {
        return 0;
    }

    return l_FileStat.st_size;
}

But I don't see the benefit, both returns generate an lvalue which is returned, except in the latter you have two code points to define the value, if anything I would argue you loose the ability to debug "l_result" from the first version, you can debug it before, during and after operating upon it... Where as the latter, you don't know the return value until the return, which results in the allocate and return.  And again in both cases the assembly produced is identical.

So, the load function, how could it be written with returning as soon as you see a problem?... Well, how about this?

LoadResult LoadFileReturning(const std::string& p_Path)
{
    LoadResult l_result;

    if ( p_Path.empty() )
    {
        return l_result;
    }
     
    auto l_FileSize(GetFileSize(p_Path));
    if ( l_FileSize == 0 )
    {                     
        return l_result;
    }

    FILE* l_file (fopen(p_Path.c_str(), "r+"));
    if ( !l_file )
    {
        return l_result;
    }

    Buffer l_temp { l_FileSize, std::make_shared<byte>(l_FileSize) };
    if ( !l_temp.second )
    {
        fclose(l_file);
        return l_result;
    }

    auto l_ReadBytes(
        fread(
            l_temp.second.get(),
            1,
            l_FileSize,
            l_file));

    if ( l_ReadBytes != l_FileSize )
    {
        fclose(l_file);
        return l_result;
    }

    l_result.emplace(l_temp);

    fclose(l_file); 

    return l_result;
}

We have six, count them (orange highlights) different places where we return the result, as we test the parameters, check the file size and then open and read the file all before we get to the meat of the function which is to setup the return content after a successful read.  We have three points where we must remember and maintain to close the file upon a problem (red highlights).  This duplication of effort and dispersal of what could be critical operations (like remembering to close a file) throughout your code flow is a problem down the line.

I very much know I've forgotten and missed things like this, reducing the possible points of failure for your code is important and my personal preference to not return from a function early is one such methodology.

Besides the duplicated points of failure I also found the code to not be communicating well, its 44 lines of code, better communication comes from code thus:

LoadResult LoadFile(const std::string& p_Path)
{
    LoadResult l_result;

    if ( !p_Path.empty() )
    {
        auto l_FileSize(GetFileSize(p_Path));
        if ( l_FileSize > 0 )
        {                        
            FILE* l_file (fopen(p_Path.c_str(), "r+"));
            if ( l_file )
            {
                Buffer l_temp { l_FileSize, std::make_shared<byte>(l_FileSize) };
                if ( l_temp.second )
                {
                    auto l_ReadBytes(
                        fread(
                            l_temp.second.get(),
                            1,
                            l_FileSize,
                            l_file));

                    if ( l_ReadBytes == l_FileSize )
                    {
                        l_result.emplace(l_temp);
                    }
                }                

                fclose(l_file);
            }
        }
    }

    return l_result;
}

This time we have 33 lines of code, we can see the stanzas of code indenting into the functionality, at each point we have the same decisions taking us back out to always return.  When we've had a successful open of the file we have one (for all following decisions) place where it's closed and ultimately we can identify the success conditions easily.

I've heard this described as forward positive code, you look for success and always cope with failure, whilst the former is only coping with failure as it appears.

I prefer the latter, ultimately it comes down to a personal choice, some folks argue indenting like this is bad, I've yet to hear why if the compiled object code is the same, you are communicating so much more fluently and have less points of possible human error in the code to deal with and maintain.

From the latter we could pick out reused code and we can target logging or performance metrics more directly on specific stanzas within their local scopes.  Instead of everything being against the left hand indent line.

Now, I will happily tell you it hasn't been a comfortable ride to my thoughts on this topic, I started programming on a tiny screen (40x20 characters - Atari ST Low Res GEM and when I moved to DOS having 80 character widths I felt spoiled.  Now we have tracts of huge screen space, arguing you need to stay on the left is moot, you don't, use your screen, make your code communicate its meaning, use the indents.

And yes, before you ask, I was doing things this way long before I started to use Python.

Tuesday, 22 May 2018

Introduction to C++ : Starting C++ Series Part 1

A few of you maybe aware of the book on Python I wrote, and published, last year?  And I've had at least one reader get in touch for a second part.  Unfortunately my gaze has passed over Python and returned to where I live.  The world of C++.

I have a particular problem with the C++ developers I'm meeting of late, they're either simply not C++ programmers, being an actual mix of good and bad C programmers or just not programmers at all (in one case).  Then even when they are very good C Programmers, there's been a mix of the up-take on ideas and feature benefits of modern C++ itself.

Its to and for these fair folk I have begun to write about C++, a new book, based on my own real experience but tempered with where I believe teams and individuals are going wrong when converting their skills to modern C++.

For the programmers reading here now, it starts with a chapter zero... Lets take a sneak-peek....



Chapter 0: Introducing C++

It is incredibly hard to introduce the C++ programming language without at least the most cursory glance at its direct predecessor C. C was created by Dennis Ritchie whilst at Bell Labs sometime between 1969 and 1973, in 1978 Dennis co-authored a book, the book, on the C Languages with Brian Kernighan. Together known as K&R, Kernighan and Richie's book was a great success,
spreading C into being, arguably, the most widely used programming language at the time, and still in that top ten league today.

The success of this first publication, its relative low price of entry into the fast developing world of C for an ever growing number of different machines, really did set C as the language to learn for a very long time.

"C has all the basic elements for expressing computation, it has iterations, it has data types, it has functions and that's it. It doesn't get into the game of expressing abstractions" - Bjarne Stroustrup.

So the world was until 1980, when a talented programmer by the name of Bjarne Stroustrup;  working just down the hall from Brian Kernighan at AT&T began a project. He called it "C with Classes". Intended as a natural extension to C, it inherited a large part of the C language syntax as well as many of the mannerisms of C and general purpose computing from the late 1970's.

The concept of "C with Classes" was to furnish users of C with a way to allow the representation of abstractions, if one wanted to represent a car in code they could define something called a "Car", it could have internal values to represent it's speed, direction of travel, the fuel level, everything that we think of as a car could be expressed within a class. In C one has no such way to encapsulate such functionality with any form of familiarity.

Certainly in C you can have a set of variables which represent the exact same things, you can name them to have a meaning of "fuel level", however they are not within anything known as a "Car" you as the programmer has to remember where all these values are, what they are called you have no easy of recall to get back to the values you are using, the concept of a class (what today we also call an object) was one of the major drivers behind the work being carried out.

The name however, "C with Classes" was not as succinct as one might desire, and indeed a friend of Stroustrup suggest he change the name of the language to C++, as the "++" function literally means to add one, an increment. The new language is an increment over the old.

Since then C++ has been ever evolving, in 1998 the first standard version of C++ was codified, from pre-existing attempts to unify the language by both specific vendors of tools for the language (such as Borland, Microsoft or Lattice) and industry bodies (such as ANSI). Published as ISO/IEC 14882:1998 by an ISO working group, C++98 drove home that C++ was at last truly diverged from C. A language in its own right, and something which had to be thought about differently.

I myself started to learn C++ in 1996, the difference in the community before the 1998 standard and afterwards was palpable, since then four other standard have been released. 2003 brought C++03, 2011 brought a working set of revisions ultimately called C++11, but also known as C++0x (due to the new standard taking so long to finalised, it was drafted and promised many times between 2004 and 2009 hence "0x). 2014 saw a further release as C++14, then 2017 saw C++17. The next revision is slated for 2020, it's name is yet to be decided, though good money could be placed on C++20.

From this release schedule we can see the acceleration curve, the faster and faster pace at which C++ has and is diverging from it's roots in C. It has matured, expanded and at each new update become more inclusive of functionality based on abstractions.

Today you can pick up modern C++ and it contains much more than the sum of its parts, you can express everything you could in 1978 in C, but so very much more.

Unfortunately, this success in expanding it's expressive nature, incorporating ever more abstractions and structures from computing, and every-day life, is tainted with some sadness, for as much as C++ strives and drives and builds every upwards, forever it has this seemingly unbreakable umbilical back to C.

You can pick up any C++ compiler from any vendor today, on pretty much any platform, and input a huge swathe of code written not in C++, but still written in C. You can elect to put this very book down right now, pick up a copy of the same book published by Kernigham and Richie in 1978 and produce code which works and work-ably solves some parts of the computational challenges you
face.

However, none of that code will be expressed in the powerful, elegant, I think beautifully powerful manner in which C++ allows you to. Abstraction, encapsulation, expressive representation of the real world in code in a manner which betters your understanding of the topic (as the programmer) but also allows others, non-programmers, to comprehend the devil within the detail of programming a modern computer.

Wednesday, 2 May 2018

The Best and the Worst : Working with Genius Programmers

A long time ago, in an office far away from where I now sit, I once worked with a chap I still refer to as the best programmer I've ever met.
This was a guy who could take the whole code base, in Delphi, home and over a single weekend re-write it in Java.

This was a guy who I saw, from scratch, write a C controller for an embedded PIC to capture an image from a supposedly incompatible TTL driven camera and then an analyzer for the captured images which would detect and show motion, making for our common employer their best ever selling product a cheap security motion detection system, which didn't rely on relatively expensive high resolution cameras.

It was awe inspiring as a newly graduated programmer, whom had a huge background in DOS programming, but whom had never worked in Enterprise level development before.

I sat next to what I still regard as near genius.

This very same chap was also the worst programmer I've ever worked with.

Because he was so highly functioning he never needed to document his code, fine I hear you cry, good code should be self documenting; and you're absolutely right; the problem?  This guy also got bored so so quickly, so he used to tell himself stories in his code.

Yes, Robert Jordon eat your heart out, this guy wrote epic fantasy on a grand scale, across hundreds of thousands of lines of code, in Delphi, C, C++, Java and even in HTML which I saw him churn out, it was all a gobbledygook puddle of story telling rambling mess.

But the code worked, the managers didn't care that it was gibberish; at least not at first; because they could churn out product to the anticipating masses of customers.

Such a prolific talent, he had so many fingers in so many pies, he was invaluable, key man, the man, the one person every project started with.

The result?  Every single code base he touched was tainted with this un-maintainable morass of code.  Which an ever increasing march of cheap graduate programmers, like my then self, had to then decipher, maintain and coral.

Often the time it took to bring a project into some semblance of order would be three or four times more than it took that one original chap to write, this did not go unnoticed and managers rightly pointed their fingers to ask the question "How could you not keep up?"

I however was the first such junior person with a voice, I've always had a voice, and I pointed right back "How could you let us get into this mess?"

I dated to question, sweep, and change the code, I dared to spend time even just aligning the code correctly.  No JetBrains formatting (or resharping) tools, very few tools existed to cover the whole pantheon of mess we were now wrestling to stay a head of.

Daring to question, change, read and challenge the talented one resulted in his changing his ways, he returned to some of the projects I had lead re-working, he saw the structure and the discipline within he saw that you could quickly pick up and get to work without needing to load all the software into ones wetware in a laborious re-read.

This skill, this willingness, to press the boundaries is somewhere I've oft and continue to take projects, and I do ask those I throw code at to feedback to me where they think anything needs reviewing.

I deplore any project or maintainer whom takes the grounding that they must keep things secret and keep things safe.

Drop, the epic fantasy, you're not Gollem, share, review and open the boundaries.

Tuesday, 27 February 2018

"Zeiger" The Missing C Type Link

I'm just looking at an old C Compiler, the first C compiler I ever used, actually... And, I'll be honest, I didn't live with C long, I moved into C++ pretty quickly.  So much so, I never actually read the this C Compiler's Documentation, here for posterity, is an exert...

Pre-defined Data Types
----------------------

      Type        sizeof      Bits            Range
      ----        ------      ----            -----
  unsigned char      1          8             0 to 255
  char               1          8          -128 to 127
  enum               2         16        -32768 to 32767
  unsigned short     2         16             0 to 65535
  short              2         16        -32768 to 32767
  unsigned int       2         16             0 to 65535
  int                2         16        -32768 to 32767
  unsigned long      4         32             0 to 4294967295
  long               4         32   -2147483648 to 2147483647
  <pointer>          4         32
  float              4         32   (+-)3.4E-38 to (+-)3.4E+38
  double            10         80 (+-)3.3E-4932 to (+-)1.2E+4932
  long double       10         80 (+-)3.3E-4932 to (+-)1.2E+4932
  Zeiger             4         32             0 to 4294967295


I have never heard of the "Zeiger" type, it's simply an unsigned 32 bit integer, but where does that name come from?

It's a German word, meaning pointer or hand, so I can only assume it's a pointer to some memory location and the machine this compiler is for, if I recall correctly had a 4 megabyte maximum address space, plus 64K of addressable ROM.  So a Zeiger would be useful to parse over any location in both, however it's not clearly stated in the information.

Zeiger is a reserved word in this compiler, and yet it purports to be ANSI compliant... Hmmm.... Thoughts as ever in the comments below.