Version: 1.00.01 - last update: Tuesday, July 31, 2014, 12:35:00 [fixed some minor typos and reformatted font size]
Things you should really know about VFP's compile-time constants
Intro
There are some new things I've learned about Include Files and their content lately! Here comes what could be said is common knowledge amongst VFP developers:
- It is always good to let all your classes include one .H-file!
- It is even better if these "low-level" includes are populated sparsely, including one (or more) "top-level" include files in turn.
- Defined constants cannot be redefined, unless they are undefined first! Even worse, VFP will complain about redefining constants! (Well this is only half the truth as I will show you!)
- The last point above implies that you have to maintain a well-ordered hierarchy of including include files!
A Well-Organized Hierarchy
Maintaining a well-organized hierarchy of include files means that we have to be aware of which .H-files include which others and HOW they include them!
If you ask yourself why I'm stressing the word HOW, let me tell you, there is a good reason to do so! I will show you in a minute why…
My Favorite Approach
-
The top level is the ROOT Level, which is FoxPro itself. There is the FoxPro.H file that we all know well, that should be made our top-level include.
-
The second level is the Framework Level – in my case this is the FoxQuill Framework. Thus, there is a the FoxQuill.H file, which in turn includes some further Framework Includes.
-
The third level is the Project Level – this is where you should put your project related constants.
-
Finally, the forth level is the ClassLibrary Level – each class in a class library should at least share one common include file, or include its own.
My favorite approach is to give each of my include file levels a name.
Base Include File Schema
The above leads us to the following Base Include File Schema:
FoxPro.h –> is included in –> FoxQuill.h –> is included in –> ProjectName.h –> is included in –> ClassName.h
You may do the same thing with your compile-time localization include files. E.g. like this:
General.En.h –> is included in –> Framework.En.h –> is included in –> ProjectName.En.h –> is included in –> ClassName.En.h
How VFP Resolves Constant Definitions
Let's have a look on how VFP resolves compile-time constants defined in your include files, especially, how VFP deals with duplicate declarations!
There is one error, I bet, you have already seen more than once:
This error (#1725) simply tells us that we've just tried to redefine an existing constant definition. Sometimes it's no fun to find the place, where the first definition takes place, especially when working with someone else's legacy code that uses a lot of nested include files! Sometimes, the best we could do is to add a quick #UNDEF statement before we #DEFINE our own constant. But this may break some legacy code relying on the old value! These kind of weird situations are all stemming from some simple mistakes made by the guys that wrote the legacy code a long time ago, because they were unaware of how to nest include files the right way!
The Base Include File Schema introduced above is a hierarchical one. That means, compared with a class inheritance tree, that higher level include files contain more common/global definitions. The deeper we dive into our include file hierarchy, the more specific our defines should become.
As Short Quiz
Let us approach the essence of this writing playing a little pop quiz. Try answering the following questions without consulting VFP's documentation or trying them out in VFP's command window!
Question#1: What code line below caused the Error Message Box above to pop up with error#1725 ?
#DEFINE FRAMEWORK_NAME "FoxQuill " #DEFINE FRAMEWORK_NAME "FoxQuill " #DEFINE FRAMEWORK_NAME "foxquill"
VFP's help topic #DEFINE ... #UNDEF Preprocessor Directive states it: "You can redefine a #DEFINE only if you do not change the value. If you change the #DEFINE to a different value, Visual FoxPro generates an error." That's why the right answer is: "The second line!". If you have answered in addition to that: "The 3rd line will cause the same error, too!", you will get an additional bonus point :-)
Question#2: Does the second line below generates an error?
#DEFINE FRAMEWORK_NAME "FoxQuill" #DEFINE FRAMEWORK_NAME "foxquill"
The right answer is: "Yes!". "foxquill" == ""FoxQuill" is not true because it is a different value for VFP's preprocessor (that doesn't care about your SET EXACT setting). We cannot redefine a constant to a different value. Thus, FoxPro will complain about it!
Question#3: Assume, we have an include file named FoxQuill.h with the following one line content:
#Define FoxQuill_Version V9.0
Now, look at the code lines below (without cheating/testing it!). What do you think? Will there be an error, and if so, which line will cause it.
#Define FOXQUILL_VERSION "V9.0" #INCLUDE foxquill.h && file is accessible (in our search path) #Define FOXQUILL_VERSION "V9.0"
The right answer#3 is: "No!". This code fragment will compile without errors! If you guessed it wrong, don't panic, here's another chance to collect some more points ;-)
Question#4: Let's reuse the above"FoxQuill.h" include file. Look at the code lines below. What do you think? Will there now be an error, and if so, which line will cause it this time?
#INCLUDE foxquill.h && file is accessible in our search path #Define FOXQUILL_VERSION "V9.0" #Define FOXQUILL_VERSION V9.0
The right answer is: "Yes! Line#2 will cause an error". The first local define is a redefinition of the FoxQuill_Version constant defined in our foxquill.h file from V9.0 to "V9.0". BTW: If you comment that line out the code will compile without errors, you should know why by now.
Be honest, how many points did you gather without cheating? Let me tell you, before I examined VFP's preprocessor behavior thoroughly, I had no clue!
Nesting Include Files as Usual
I was used to nest my include files like this:
* Content of MasterInc.H #Include FoxPro.h #Define MyFrameVersion 1.0 #Define MasterPassword "Rumpelstielschen" *… many other definitions to follow * EOF MasterInc.H * Content of ProjectInc.H #Include MasterInc.H #Define MyAppName "AppSpy.Exe" #Define MyAppVersion 1.0 #Define AppPassword "VerySecret" * … many other definitions to follow * EOF ProjectInc.H
If you place your nested #INCLUDEs at the top of your include files you loose the feature to overwrite any definition made in those 'higher level' include files. On the other hand, using the approach above, you can rely on VFP complaining about any redefinitions you may have introduced in you actual include file unintentionally.
Actually, the latter is a feature I do not miss very much, if at all!
A Better Way of Nesting Include Files
Let's reorganize the above include files like so:
* Content of MasterInc.H #Define MyFrameVersion 1.0 #Define MasterPassword "Rumpelstielschen" * … many other definitions to follow #Include FoxPro.h * EOF MasterInc.H
* Content of ProjectInc.H #Define MyAppName "AppSpy.Exe" #Define MyAppVersion 1.0 #Define AppPassword "VarySecret" * … many other definitions to follow #Include MasterInc.H * EOF ProjectInc.H
Now, if we overwrite, lets say, the framework version like so:
* Content of ProjectInc.H #Define MyFrameVersion 2.0 #Define MyAppName "AppSpy.Exe" #Define MyAppVersion 1.1 #Define AppPassword "VarieSikret" * … many other definitions to follow #Include MasterInc.H * EOF ProjectInc.H
Foxpro will not complain about the redefinition!
even better:
VFP will disregard the assignment made in the higher level MasterInc.H file completely.
As a logical consequence, we don't have to use any #UNDEFINE statements at at deeper nesting level any longer. Look at the content of the ProjectInc.h file above: The #Define MyFrameVersion 2.0 definition is the first time definition, which we know cannot be redefined later without releasing it beforehand.
Bug or Feature?
I am not sure if this is a feature or a bug in VFP's preprocessor! But my findings have proven it:
You can include duplicate redefinitions from another include file AFTER you have defined your primary ones! But not the other way round!
Therefor, if you accustom yourself to including higher level include files always and only at the end of your lower level include files, you will gain some kind of OOP-ish overwrite in subclass feature, and in addition to that, you will spare coding time (no #UNDEFINEs any more)!
Another Cool Preprocessor Feature
Another feature of VFP's preprocessor is widely unknown/disregarded! Although VFP's help file tells us: "Compile time constants are not recognized when placed within quotation marks" (see help topic #DEFINE ... #UNDEF Preprocessor Directive), this is not the whole truth!
Everybody knows, VFP recognizes three kinds of string delimiters, but VFP's preprocessor does only recognize two of them! Strings that are delimited with square brackets like [V9.0] are processed by VFP's preprocessor, indeed!
Have a look at he following lines:
#DEFINE FOXQUILL FoxQuill2 ?[Welcome to FOXQUILL] ?[Welcome to foxquill] ?[Welcome to FoxQuillFramework] ?[Welcome to !foxquill.Framework] ?[Welcome to *foxquill-Framework]
They produce the following output:
We can observe two things: While looking for replacements VFP's preprocessor does a case-insensitive search (1), and always matches whole words(2)!
Do not Pollute Your Include Files
What has the above to do with polluting include files? Well, what are you normally doing if you want to use a string constant in your project files all over the place? Right, you will define it in one of your include files maybe like this: #Define FRAMEWORK_NAME_STRING "FoxQuill" just in case you will decide one fine day to give your work another name, which is a good thing to have in place! Now, in your code you may address your constant like so:
oForm.Caption = "Welcome to the new version of the " + ; FRAMEWORK_NAME_STRING + " Framework!"
Wow, this is a lot of code to type in! Now have a look at the non-polluting solution. First, drop the include file definition completely, then code:
oForm.Caption = [Welcome to the new version of the Foxquill Framework!]
That's all it takes! You are still as flexible as when using the first approach! If you ever will decide to change your framework name to something else it will be sufficiently early to add some #Define FOXQUILL FoxQuill.Net to your include file that day in the future!
This comment has been removed by a blog administrator.
ReplyDelete@subbu raj
ReplyDeleteI do NOT accept brain dead comments only to let you post a link to your commercial VFP2NET site! If you are willing to pay for adding a banner of your company on my blog site, let me know...