forums.silverfrost.com Forum Index forums.silverfrost.com
Welcome to the Silverfrost forums
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Reduce maintenance headaches (conditional compilation)

 
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> General
View previous topic :: View next topic  
Author Message
wahorger



Joined: 13 Oct 2014
Posts: 1217
Location: Morrison, CO, USA

PostPosted: Wed Nov 29, 2017 10:01 pm    Post subject: Reduce maintenance headaches (conditional compilation) Reply with quote

A few weeks ago, I was struggling with BLOCK DATA and its ilk. Less struggle now, by using a little-discussed feature of FTN95.

I already have all of my labelled common declarations in separate .INS files. In the past, I'd also have a BLOCK DATA sub-program to do initialization. This meant that I had to modify two separate files if I added a variable to the COMMON (one for the declaration, the other for the BLOCK DATA initialization).

Now, I am using only one file, including the initialization statements in the .INS file.

But, hold on you say! If you use this .INS file in multiple routines with initialization, you'll get linker errors!

That is true, but only if the data statements are actually compiled in all cases. Here's where BLOCK DATA and conditional compilation can be effectively used.

You create your TEST.INS file like:
Code:

! Create data here
        REAL MY_GOOD_DATA
#ifdef INIT_ME
        DATA MY_GOOD_DATA/3.1415/
#endif
        COMMON/MY_BLOCK/MY_GOOD_DATA

Everywhere you need this, you INCLUDE the TEST.INS file. Since you have not defined the symbol INIT_ME, the DATA statement is not compiled. In your BLOCK DATA sub-program (CBLOCK_TEST.F95), you write:
Code:

        BLOCK DATA CBLOCK_TEST
#define INIT_ME
        INCLUDE 'TEST.INS'
        END

You compile the block data program with the flag /CFPP to enable conditional compilation, and link it in with your other routines.

You don't actually need the BLOCK DATA sub-program. Simply add the #define statement in ONE AND ONLY ONE of your routines where the TEST.INS is used, and it will do the same job as the BLOCK DATA.
Back to top
View user's profile Send private message Visit poster's website
DanRRight



Joined: 10 Mar 2008
Posts: 2813
Location: South Pole, Antarctica

PostPosted: Thu Nov 30, 2017 2:46 am    Post subject: Reply with quote

I'm sorry for questions but I see the long many year stream of messages which puzzle me. Can anyone decipher to me what Wahorger is trying to accomplish? I got no clue what are these block data, conditional compilations and #ifdef good for? As I never heard this for 40 years from anyone else, and don't often hear the responds all sounds like a forgotten foreign language, or I missed something?

40 years ago appeared Fortran 77, before was Fortran66 and Fortran4. I doubt the discussed stuff of as old as the code looks pretty advanced. So what is this about?
Back to top
View user's profile Send private message
wahorger



Joined: 13 Oct 2014
Posts: 1217
Location: Morrison, CO, USA

PostPosted: Thu Nov 30, 2017 5:39 am    Post subject: Reply with quote

Dan,

Let me try to explain.

I have a commercial product which does data entry and data processing for a niche market. Originally written in the 1980's in FORTRAN 66 for microprocessor systems (CP/M to start, then on to MS-DOS, and beyond), the program now utilizes about 80 labeled common blocks, and has over 300 subroutines/functions that perform a wide variety of functions, from data entry through coordinate datum transformations. Many of data variables in COMMON must be set to an initial value to allow the code to establish the initial conditions properly. Since I don't know what the user might want to start with first, all data has to have a predictable initial condition. Simple.

Since FTN95 does not automatically initialize all data to zeroes in labeled common (the old CP/M and MS-DOS FORTRAN compiler/linker did exactly this), one has two options. First is to execute code to initialize, variable by variable, the COMMON, or second, to use a DATA statement to initialize these data. The latter is more efficient, since the data are pre-loaded at run-time. Also, separate program executables might require more or fewer COMMON's to be initialized. So, executing code to do this requires multiple "variants" to initialize the required data.

One can do this the "normal way" using a BLOCK DATA sub-program. In the old days, BLOCK DATA was the only way data could be initialized in labeled COMMON. FTN95 does not have this restriction. But if you use it, you have other restrictions.

Which takes one to the maintenance of the initialization data. Does one like to edit two files when adding a variable, or perhaps add an all new COMMON block? One file for the COMMON and variable declarations, and the other for the BLOCK DATA and the initializations? No, having a single file that can be used for both purposes would be preferable. Not required, just preferable.

Enter conditional compilation, something that "C" compilers (and others) have also had for a long time. FTN95's implementation is quite adequate for many tasks. I'll stick with this one task for right now.

By placing conditional compilation elements into the INCLUDE'd file, I can control whether or not a particular section of code gets compiled or not. In this case, the control is whether or not to compile the data initialization.

If I choose to set the conditional compilation variable to a DEFINE'd state, then the compiler will compile the DATA statements, thus initializing the labeled COMMON. But, one only wants to do this one time. If one does this more than once, then the linker will fail with a multiply defined symbol error. Try it. These errors are actually hard to find when ten's of variables are involved across multiple compiled units.

So, I've chosen to use BLOCK DATA, and to INCLUDE the variables, COMMON block declarations, and DATA initializations with the DATA initializations "turned on" for compilation by using conditional compilation.

If your programs are not structured to use labeled COMMON, and/or you do not need to have data pre-initialized via BLOCK DATA (or any DATA statements), then none of this will matter to you, or will it have any meaning.

It does in my situation, and others may also have a need to solve a similar problem.

Or, one may want to do, with conditional compilation, something a bit more intriguing. In my case, creating an evaluation version of the software package. I chose to limit the evaluation version to a specific number of various entities that can be created, edited, or displayed. By using conditional compilation, I change the array sizes and PARAMETER limits that control how the software interacts with the user.

So, with minimal changes to the code (also controlled by conditional compilation), I can create this evaluation version. Same program modules, same INCLUDE files, just slightly different settings.

I hope this helps your understanding of my process and solutions.

Bill
Back to top
View user's profile Send private message Visit poster's website
DanRRight



Joined: 10 Mar 2008
Posts: 2813
Location: South Pole, Antarctica

PostPosted: Thu Nov 30, 2017 11:57 am    Post subject: Reply with quote

Bill,

Does your code run multiple times without exiting to Windows and due to that you need to turn the code to initial state each time you run new variant?

If not, then at least in 32bit case (64bit one could be different so far) I suppose that /ZEROIZE compiler switch turns variables to zero including variables in common blocks at the time they are used. This never crossed my mind even for a millsecond in last almost 30 years i am using this compiler with no problems ever noticed (i'm not using block data and equivalences).

For the case the code numbercrunches multiple variants one after another without exiting to Windows would be great to have an option of simple turning the code to initial state before the run. I suggested to Silverfrost to think about ZEROIZE@ function to zeroise all dynamic variables (leaving variables in DATA structures intact). But since this is not yet available the way i do is just routinely zeroising and giving initial and boundary values to each and every variable before the running. But this is routinely needed anyway for partial differential equatiuons (setting initial and boundary conditions).
Back to top
View user's profile Send private message
LitusSaxonicum



Joined: 23 Aug 2005
Posts: 2388
Location: Yateley, Hants, UK

PostPosted: Thu Nov 30, 2017 12:41 pm    Post subject: Reply with quote

Bill,

I started out with compilers that didn't support BLOCK DATA, and so I never got hooked on DATA statements either. When I did initialisation it was all done with assignment statements. Of course this takes execution time, and I did know that somewhere there were compilers that did have BLOCK DATA, and so I have tended to do 'assignment initialisation' in a subroutine, usually called BLOCK_DATA !

As far as Windows applications goes, pre-initialisation only works for the first run through. The is true of Dan's approach, the FTN95 norm of setting static variables to 0, etc. However, with all initialisations done by assignment, starting a new 'document' (in a single document interface program) is only a matter of a single subroutine call. You can't repeat an ordinary BLOCK DATA.

It is probably more complicated with a multiple document interface program, but then it is difficult to imagine that BLOCK DATA works well for that.

Eddie
Back to top
View user's profile Send private message
wahorger



Joined: 13 Oct 2014
Posts: 1217
Location: Morrison, CO, USA

PostPosted: Thu Nov 30, 2017 1:32 pm    Post subject: Reply with quote

Eddie, thanks for your posts. I don't use an MDI interface. It's much more simplistic than that!

Once my user starts the code, and the configuration file is processed, this sets the configuration for this run. They remain in the program, exercising various options/features until they are ready to exit. One section might get run multiple times, and in all those cases, there are some data that need to be reset to an initial value. This is done programmatically. The number of variables that need to be reset are, mercifully, few compared to the number of variables that need an initial value.

Should the user wish to process another configuration, they run the program again. This actually allows for multiple instances of the program to be running on different data sets simultaneously.

That aside, the /ZEROISE works with /UNDEF, but does NOT initialize COMMON data. Also, with a lot of CHARACTER data, zeroing memory does not quite fit the requirement for this kind of data.

Also, I have a couple of very large, static tables of data that contain character, r*8, and integer data. Some of these tables have in excess of 54000 rows. Yes, I could read this data in from a file, but it is better to have the data localized to the executable, and only present in those program units that need it. Thus, BLOCK DATA. Since these data can be used in multiple program modules without regard for which module is run first, the use of BLOCK DATA means I don't have to create a routine reference in each module that uses the data and have it either detect the data has been loaded, or force load it each time.

The original code, being developed and tested on a FORTRAN 66, 64KB CP/M system, required an overlay structure for the program itself. Data to be retained from module to module required a labeled COMMON block. This overlay structure was also supported on a PDP-11 system (again, memory limited) allowing the code to run in both environments with the only changes required for syntactical reasons (like INCLUDE syntax). Thus, to retain the maximum amount of code without requiring a change, the COMMON structure has been retained. For better, or worse.

When it makes sense, I like the more classic technique of passing variables and arrays (and TYPE's) as arguments.
Back to top
View user's profile Send private message Visit poster's website
LitusSaxonicum



Joined: 23 Aug 2005
Posts: 2388
Location: Yateley, Hants, UK

PostPosted: Thu Nov 30, 2017 3:22 pm    Post subject: Reply with quote

It's good to see someone else keeping their old code alive.

Parameter passing to subroutines used to eat up the stack, which is why I never liked it much, but like so many old habits such as overlaying, having a lot of RAM has rendered them obsolete. The past increasingly looks like that foreign country where things are done differently.
Back to top
View user's profile Send private message
wahorger



Joined: 13 Oct 2014
Posts: 1217
Location: Morrison, CO, USA

PostPosted: Thu Nov 30, 2017 5:19 pm    Post subject: Reply with quote

Thanks, Eddie!

Old code is good code. Until it's not any more.....

As I look back, I see things I did which made sense at the time, but now need to be (and have been) totally changed. There are better ways.

An additional 30 years of experience in real-time and mission critical spacecraft software have honed an edge within me. Anything I can do to improve maintainability or readability is up for implementation. I've done several things using the compiler as a maintenance-reducing tool to make the code as transparent to both KIND and data typing as possible. Works really well in the instances where I've found a need.

Improvement is fine. Just so long as one does not break it! Catastrophic "improvement" is NOT fun.
Back to top
View user's profile Send private message Visit poster's website
JohnCampbell



Joined: 16 Feb 2006
Posts: 2554
Location: Sydney

PostPosted: Fri Dec 01, 2017 12:52 am    Post subject: Re: Reply with quote

wahorger wrote:
As I look back, I see things I did which made sense at the time, but now need to be (and have been) totally changed. There are better ways.

Unfortunately, I sometimes look back and don't see why they made sense at the time. Changing to a new "better" way shows what I have forgotten.
Back to top
View user's profile Send private message
LitusSaxonicum



Joined: 23 Aug 2005
Posts: 2388
Location: Yateley, Hants, UK

PostPosted: Fri Dec 01, 2017 2:19 pm    Post subject: Reply with quote

And sometimes the new way is worse than the old.

Now, where do I buy a plane ticket from Heathrow to New York that will get me there in just over 3 hours?

Where does an astronaut find a rocket to take him to the moon? (Or her?)

Why don't any cars still spray water on the windscreen BEFORE the wipers work (to stop them scratching the glass with dry grit)? And why can't I find a car that puts cool fresh air on my face while drying my trouser legs with warm? (That's a function of being out of doors in wet weather. You can't do site inspections from a computer screen).

Sometimes they are the same, but puffed up as better. Need I ask why one needs CYCLE and EXIT instead of GOTO which does the same thing? (Plius hundreds of other things that are different, but not better. As Paul would say, look at the Assembly code).
Back to top
View user's profile Send private message
DanRRight



Joined: 10 Mar 2008
Posts: 2813
Location: South Pole, Antarctica

PostPosted: Sat Dec 02, 2017 12:25 am    Post subject: Re: Reply with quote

wahorger wrote:
Since FTN95 does not automatically initialize all data to zeroes in labeled common (the old CP/M and MS-DOS FORTRAN compiler/linker did exactly this), one has two options. First is to execute code to initialize, variable by variable, the COMMON, or second, to use a ....

So, I've chosen to use BLOCK DATA, and to INCLUDE the variables, COMMON block declarations, and DATA initializations with the DATA initializations "turned on" for compilation by using conditional compilation.


Ok, one thing we found not needed: the /zeroise presets to zero your common blocks too with just one key and all is done. For multiple use setting data like Eddie or John (me too, and I have >1000 subs code and uncountable amount of command blocks and modules, started in 1978 using DEC PDP11/70 Fortran. Before that Fortran I used had even no name as it was some domestic subset for punch card monsters) doing is clearly the way to go as it is more universal method while block data doing that only ones. All things used in the code have to be easy in programming sense if the code becomes very large or you will be buried in workaholic's lifestyle.

Now what is conditional compilation and why it is important to you while no one else bothers? I'm trying to remember if I forgot or even knew what is it and the only I do by wast amount of day time is searching for errors and recompilations
Back to top
View user's profile Send private message
wahorger



Joined: 13 Oct 2014
Posts: 1217
Location: Morrison, CO, USA

PostPosted: Sat Dec 02, 2017 6:58 am    Post subject: Reply with quote

DanRRight, thanks for the reply.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> General All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group