UK Horse Racing's Data Analyst Tool
Ghost In The Machine - oReport and Pointers
I was asked to explain a little about what this oReport variable was in the code and this is my attempt to explain a little more about programming, particularly Pointers and how the variable oReport and, ultimately, oNodeHandler work in the DA Tool.
The Nitty and the very Gritty
WARNING: Complex Stuff Ahead...
This stuff that I am about to go through will be familiar to those who are used to other programming languages such as C and C++ or those which have done any form of Windows code development tools arrived which are basically click and make an application.
We've gone through in the past various data types. We have Long and Integer for integer types (the difference being that Long takes up twice the memory as Integer and so the range of values it can hold is larger). (Oh, whilst I am here, there is never any need in any form of programming on a Windows machine to use Integer rather than Long because not only is the range of values available significantly greater but using Long is slightly faster than Integer. This is down to how the data is stored internally but as a rule these days with more memory than before the Integer data type is redundant.) And we have Doubles for real numbers, we have String for strings and so on.
I think we're all happy with the idea that we can define a variable with the Dim statement which then allocates memory for the variable and then the run time compiler then keeps track of what lives in there. All very good and straightforward and I think we're happy with this idea and if anyone has ever written a line of code with a simple data type like those I have listed then I think it's fair to say that there is sufficient understanding.
These data types are all known as STATIC data types. A Double is a Double and it shall ever more so be a Double. This is the bread and butter of our stuff that we have done today.
Next up are POINTERS. Anyone learning VB or VBA from a book can put their book down now because these won't be in there. The Official Line from Microsoft is that there are no pointers in VB and VBA. This is complete bollocks and I have fallen out with Microsoft official support on this about a decade ago.
There are things called OBJECTS. We may have heard of this Object Orientated Programming which is common these days and without which it would have been impossible to code and maintain the Model and the Data Engine. It's a complete different way of working than we had in the late 60s, 70s and early 80s. An object is a thing; we see these objects in our Excel Applications as things like Spreadsheets, Workbooks, Cells, Ranges, Fonts and any controls such as check boxes, buttons and so on. In Word we can have Documents, Headers, Paragraphs, Sections and the like.
An Object, as I have said, is a thing. If you can point your finger at it in an Office application or in Windows then it will be an object: furthermore an object can contain other objects which can go on to contain others and so on. Apart from those objects that we can point our fingers at there are objects that we can write in code which don't appear to be tangible objects like a paragraph of text in Word or a Cell in Excel can be said to be tangible. One such object in the DA Tool is the cls_OFFICIAL_Mission_10; this isn't a tangible object as much as a Cell object is but it's an object (well, it will be when it's created).
Now, you will have heard of CLASSES. A Class is similar to an Object but only in that the Class is a blueprint for an Object. The Class is what is designed and the Object is what is built from the Class. A real-world example would be a car being an object that is created using a car blueprint.
In the DA Tool I have declared a variable called oReport. It is defined in in modGlobals as
Dim oReport as Object
In the modLink we do something with this object definition by creating an instance:
Set oReport = New cls_OFFICIAL_Mission_10
Now, bear in mind that cls_OFFICIAL_Mission_10 is a CLASS. We know that as it's listed in our Class Modules. What this line of code does, when executed, is to create or instantiate an Object based on that Class.
What we have done it to turn all that code that's in the cls_OFFICIAL_Mission_10 class into a Thing: an Object.
This is where I will deviate from the Official Literature because the Official Literature is wrong. So throw away the books at this point because I will explain what's happened here. If one reads any books which are derived from the Words of Microsoft then they will have it that the object you have created is called oReport'
The books will tell you that the Object created is called oReport. This is wrong, wrong, wrong and downright bloody well wrong.
What you has really happened is that you have created an Object, yes. That object is somewhere in memory: we don't know where and, what's more, we don't care where. However what has also happened is that there is a POINTER called oReport (which is just a variable in memory) and all that it does is to 'point' to where the object is.
All operations which one then does in relation to that object is done via the pointer, oReport. It is essential to understand that all operations on objects have be done via a pointer because the pointer is the only mechanism that knows where these objects are.
As we can see in the DA Tool that the oReport pointer can point to any number of classes; the developer chooses which one in the modLink module by commenting out all but the one that is required. There's a class called clsVectis_1, for example, and we could have oReport point to that object instead.
The thing is, when I designed the DA Tool, I didn't know which class would be instantiated into an object. It could have been one of dozens and, over time, the list of possible classes has doubled, tripled and more. But each one is similar in one aspect; they all have a subroutine which is defined as Public:
Public Sub RaceAnalysis(nIndexStart As Long, nIndexEnd As Long, nMaxCols As Long)
Because each and every object has this routine with the same parameters I know that it can call it via the oReport pointer.
In the modMain module there is the following procedure which is called by the main loop. What happens in the main loop is that the CSV data is read and then each race is determined, which will constitute two or more horses, and then this routine is called:
Private Sub ProcessRace(nIndexStart As Long, nIndexEnd As Long, nCols As Long, sFileName As String)
oReport.RaceAnalysis nIndexStart, nIndexEnd, nCols
What is happening here is that the main loop 'knows' which row numbers of the CSV file are the first and last rows of that race (ignore the last argument, sFileName, as this isn't important here for this discussion. Ignore for now how it has determined which is the first and last rows as again this isn't important. But what is important is that we have to get this information to the Object we've created and we do it via the oReport pointer.
The oReport pointer points to the object in memory and this is where I hope that there's a public procedure called RaceAnalysis() waiting for it. If there is, and there should be, then the CSV file is processed. And what's in that routine? Why, it's the code that we write.
So you can see that by using a pointer to an Object we can create a flexible to do what we want. Writing the DA Tool without a pointer would make it nigh on impossible to write.
In the heart of every RaceAnalysis () procedure in every one of these classes is the following call:
oNodeHandler.AddResult asEntry(), nIndexStart, nIndexEnd
This is where every horse which passes our tests is dealt with. Now, the first thing to note is that oNodeHandler is another pointer. Each and every one of these classes calls the same routine. If one right clicks on the oNodeHander text and then selects 'Definition' then the cursor goes to where the pointer is defined and we can go to the head of the routine to this line:
Dim oNodeHandler As zzzLinkedListHandler
This is different to before. Just slightly. The difference is that the class that I have defined this pointer to point at is zzzLinkedListHandler rather than Object. The reason for this is that I know which class object is going to be called from there - all the result handling is one in that zzzLinkedListHander class and so I am going be calling an object of that class from our own class, such as clsVetis_1, cls_OFFICIAL_Mission_10 and so on.
Because we know which class belongs to oNodeHandler this is known as EARLY BINDING as I have bound the class to that pointer early on (i.e. in code time). Because we don't know what oReport is going to be pointing at until run time this is called LATE BINDING and this is why it has been given the generic data type of Object.
If one wished to be really pedantic about this then one could say that the oReport should be declared as a pointer pointing to an object of hitherto unknown type whereas the oNodeHandler is a pointer pointing to a object of type zzzLinkedListHandler.