Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

#DEFINE philosophy 1

Status
Not open for further replies.

Scott24x7

Programmer
Jul 12, 2001
2,826
JP
Hi All,
I'm converting some values in one of my applications to make the tabs more dynamic (when changing them). So instead of referencing them by Thisform.Basepageframe1.Activepage = 7 I want to make them constants.

So:

#DEFINE gnCompany = 1
#DEFINE gnAddress = 2
...
#DEFINE gnContact = 8

etc.

My question is, where is the best place to make the declaration? Should it be made at the Form INIT level, or should it be made higher in the structure like in the startup.prg?


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
These declaration will ONLY apply to the method in which they are declared. Also remember that if you need to change any of these declarations, you must recompile in order to make the new values overwrite the old ones.
 
Scott,

The important thing to remember is that these directives are interprted at compile time, not at run time. Basically, there are four places where you can use them:

1. In the PRG or method to which they apply.

2. In an Include file (which is essentially a file that contains nothing but the directives). You then specify the name of the Include file in the PRG or method to which the directives apply.

3. In a global Include file, which is present throughout the development environment. You specify the name of this file in Tools / Options / File Locations.

4. In a form-wide Include file. You specify this in the Form menu in the form designer.

("Include file" is more correctly called a "header file".)

Regarding point 1 above, be clear that directives are not like variables. They are not scoped to a particular routine or procedure. Their "scope" is whatever is currently being compiled.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
So would I be better of for the sake of managing the tabs in the pageframe, to use a PUBLIC instead set at the Form Level?
These are constants, so the only way they change is if I Add or remove a Tab (both of which have happened while I've been developing this app, as some philosophy has changed). It's part of why I decided to make them constants so that I can just change one value (Like Documents = 8 where it used to be Page 7). At least then I don't have to go through a bunch of code to change the value of the tab, just the one place. That's the objective anyway. I thought #Define would be the right way to go about it, but I didn't realize they scoped only to the current method.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Scott,

I disagree with your conclusion. I can see no harm in using #DEFINEs for this. In fact, I have occasionally done the same thing myself. Certainly better than using public variables.

This would seem to be a good use for a form-wide Include file, as per point 4 in my post. Since the page frame belongs to a specific form, this would seem the best place to put the #DEFINEs.

By the way, you can also have a class-wide Include file. If you are creating a page frame class that can be used in multiple forms, then put the #DEFINEs in a class-wide file, and specify its name from the Class menu in the class designer.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
What Tore Bleken says.

In that respect, to not need to write these defines in every method using them, you have the menu item Class->Include File. What .h file you include there is then valid and usable in the whole class in every method.

Just to stress it out once more: A declaration is not like a global variable scoped with a biggest scope when done in the main.prg. Declarations are only precompiler directives and used within one method compiled besides the class include file exception.

There is one more larger scoped aspect of include files, that is the "Default Include File" in the File Locations tab of the options dialog:
Help said:
Default Include File
Specifies a default header file of predefined compile-time constants included automatically with user-defined classes, forms, or form sets. This option is empty by default and corresponds to the _INCLUDE System Variable.

So it is not included in PRGs and from the top of my head I think it rather gives a headache than helping, as any specifically assigned Include File in a class overrides the default instead of extending it, I think - anyway obce I tried it it wasn't helpful. I think it can't be retrofitted anyway, it only acts as default for new classes and thus only helps you not forget to put some include into a vcx class.

Also notice, the #INCLUDE directive allows you to include include files within include files, too, so they can be separated per concern and still be loaded within each other, even wrapped with some #IFDEF #IFNDEF logic, but VFP has a nasty behaviour not easily spotted: If you specify relative paths in #Include directives in code they are not taken relative to the PRG or VCX location but relative to whatever SET DEFAULT or CD path current directory you are at when compiling. One fraework I use and one project I inherited need you to CD into project home before editing PRGs or building a project because of that and I think it is an annoyance and bad design. At this point some may disagree and state other experience, but notice this often remains unnoticed and becomes a sleeping watchdog, if you build without using the recompile all option, then most files are not processed again also in regard of misplaced header files. But when you do, suddenly the includes don't work. Also for that reason I see double header files in one project I inherited, someone squashed the issue by putting smae include files in several folders, so they are always found, but of course that becomes a nightmare to maintain as all of them need all changes.

I limit myself to include files you specify in VCX classes, as that works best and the compiler manages to find header files from the info stored in the vcx not depending on current directory when compiling. Still I make it as easy to have a complete set of files when reusing classes, so I put .h files within the libs folder of classes and not in a separate /includes/ folder, neither parallel to /libs/ nor as sub folder /libs/includes. I name includes files same as the library, though include files are to be specified per class, they are not applied to all classes of the whole vcx library, when specified within one class design session. But at least they have the advantage to be applicable in every method of the class.

Therefore I also prefer to design visual classes instead of PRG classes. You can argue and say in contrary despite needing to CD into some folder for working with a project, you can a) make that something the envirnment manager within the task pane takes care of and b) once used to it have a stronger weapon with all the detail possibilities of preprocessor directives. I mainly do without any #IF(N')DEF or other advanced preprocessor directives, though I know some applications for it, because this is a beast more than it is helpful, and I like to have my build setting for fast builds without recompile all. To take care of Tores warning, If I change the .h file I also once compile with recompile all, if the header is used in many places or "touch" the relevant classlib(s) by opening them and adding a space somwehere, other trickery is possible with foxtouch within foxtools.fll to make VFP think files need recompilation, but that's also a hack. VFP unfortunatly is not as intelligent to detect a .h change as need for recompilation of classes, as the compiler does not look from that side, it mainly looks for last changed dates of prg or vcx or vct or scx/sct etc files and not of .h files.

Overall, it's a good idea to make less use of includes and defines. I often use them for the same goal, though. Not only for page or other numbers having certain meaning, also for meanings of boolean parameters even in native functions, which yo don't find in Foxpro.h in Home(). For example

Code:
#DEFINE cnCommitCurrentRowOnly 0
#DEFINE cnCommitAllRowsOnErrorStop 1
#DEFINE cnCommitAllRowsOnErrorContinue 2 
[highlight #FCE94F]#DEFINE clForceUpdates .T.
#DEFINE clDontForceUpdates .F.[/highlight]

TABLEUPDATE(cnCommitAllRowsOnErrorContinue, [highlight #FCE94F]clForceUpdates[/highlight], ALIAS(), laErraticRecnos)

That is easier to understand than TABLEUPDATE(2, [highlight #FCE94F].T.[/highlight], ALIAS(), laErraticRecnos), isn't it? So it also pays to specify constants for other than numeric types with a meaning not showing when you don't know the parameter name is lForce - besides even that does not tell what is forced or not forced.

Bye, Olaf.
 
What I wrote overlaps with Mikes answer, but we do agree on that point, the include file option of forms and classes are best as they apply to all methods.

Besides, you still have to change the constants, when a page is removed. best way to get rid of a page without the need to change any code OR constant is to only set the page visible=.f., that also helps with any code acting on page controls etc. Don't ever assume removing something has no side effects on remaining code, even if you're the only developer involved in that page. Code acting on an invisible page will not fail and the worst thing, that could happen is the page will be made visible by code, but then you'll find this. Besides you could add a visible_assign method and disregard any value passed in that method and keep it at .F.

It surely is the worst idea to use public variables because you can't think of anything else with bigger scope than local variables, even when you tend to avoid constants, you may use properties for that matter, there always is something else than public variables having the right scope, don't forget properties when thinking along lines of variables and constants and their scope, properties are like variables. You define them at design time just like PUBLIC or LOCAL commands do in code, you can set them and change them while code runs. As they are part of an object and addressed as object.propertyname, it's logical - their scope is the object and that most often is the ideal scope of things you need for a whole object, isn't it? Just think of them as variables with a point in their name, and they are variables, too, with a very useful scope of the object they belong to and with special prefix names THIS and THISFORM they are also self contained without knowing the variable name holding an object reference, aren't they? That nomenclature makes it very obvious they are SELF scoped variables when thinking of them this way. Of course THIS.property refers to THIS in the sense of SELF or ME (as used in other programming languages), so it also is clear this is a "variable" which can be available multiple times with multiple instances of a class, without overlap, redifnitions, overwriting and all the other disadvantages of PUBLIC variables, as THIS means THIS instance and not all objects created from the same class, only THIS object.

Bye, Olaf.
 
Thanks guys. I have long understood the purpose of #DEFINE but have not previously tried to use it. And I quickly found what I thought was not at all the case. And sadly the Help file isn't that helpful in this case. I've not implemented a .h file before, so reading through the instructions, it looks to be a little finicky, but as Olaf has illustrated, I quite like that idea of making booleans more meaningful, like the clForceUpdates, and I can see where you want that essentially available everywhere.
It seems Foxpro.h may be the best place to place those constants that you would like to have as "universal" (I use that instead of "global").


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
What I would not do is extend Foxpro.h itself, though such extensions specific to VFP language are surely belonging in there. But just because there never will be VFP10 from MS it is a no go to modify any given files, just write your own (eg VFP.h) and let that include Foxpro.h, too.

Always think what happens, if others do similar things as you, you'd have variations of foxpro.h and couldn't easily merge and extend that, if you use it in your own framework and want to sell it to other VFP developers. Never assume you are the only one having such an idea and act like others are less logical about this, so you have to cope with not being able to replace Foxpro.h with your version, if they also extend it. You will always be able to add an own file, even if others have the same idea and use the same name, it'll be possible to put yours in its own specific folder. That idea won't work with a copy of Foxpro.h you extend, because it would surely collide with include of the original Foxpro.h in redifinitions leading to compile errors. So make your own new file and only put in new defines there, don't do your own Foxpro.h as modification of the original, that will just cause headache for others.

Also a reason namespaces are so important, even though VFP does not technially support such a concept.

Bye, Olaf.

 
No, FOXPRO.H is not the place to put your #DEFINEs. To do so would be to be to adopt a global solution to a local problem - something programmers should avoid.

I've already pointed out that you can create an Include file (aka Header file) that is specific to the pageframe class or to the form that holds the pageframe. One of those would be the logical place to put your #DEFINEs in this case. That way, the directives are "local" to the place they are needed.

To create an Include file, all you have to do is to create an ordinary text file, with one directive on each line. Don't put any other code in the file, except possibly comments. Save the file with an extension .H (ideally, give it the same name as the form or class). Then specify that name from the appropriate item in the Form or Class menu in the form designer or class designer respectively.

That's all there is to it.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike,

I think you have to separate two things here, constants like my proposed clForceUpdates are about the Foxpro language, in that matter Scott is not thinking of putting his page constants into Foxpro.h, I have already stated, why I'm on your side with that, even if constants are about the VFP language and not your own classes. You never change a class you get, you inherit from it and do changes in the sub or child class you create and in regard of .h files you do your own as extension by including other ones. This has its limitations, the preprocessor will only dig down 4 levels of includes, that doesn't mean after 4 include files no further file is processed, that means the levels of includes followed in include files is four. Surely enough to not need to flatten this on the level of foxpro.h

Take a look at Edit: Burkhard here is exactly using 4 levels of header files, that is exactly what is allowed.

Bye, Olaf.
 
Ah, I see.
I suppose it is about time I learned to do this properly. I can see the advantage.


Best Regards,
Scott
ATS, CDCE, CTIA, CTDC

"Everything should be made as simple as possible, and no simpler."[hammer]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top