Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

Fortex, a Fortran Runtime Executive


JUN92: FORTEX, A FORTRAN RUNTIME EXECUTIVE

FORTEX, A FORTRAN RUNTIME EXECUTIVE

A Fortran I/O-enhancement tool

Harold R. Justice

Harold is the manager of a simulation-development and software-engineering group for Dynetics. He can be contacted at P.O. Drawer B, Huntsville, AL 35814.


Fortran is the language of choice for many scientists and engineers, especially for simulation programs. Although languages such as C, Pascal, Ada, and special-purpose simulation languages like the Advanced Continuous Simulation Language (ACSL) and Simulation Language for Alternative Modeling (SLAM) have made inroads into scientific programs, Fortran's popularity for scientific and engineering applications has remained unabated.

Fortran provides I/O facilities in the form of read/write (or print) statements used as primitives to develop the user interface (UI). The application developer can easily spend a large amount of development time programming the UI with hardcoded read/write and format statements. Most Fortran environments make available a symbolic I/O method called NAMELIST. Although NAMELIST provides a symbolic means to set the values of the variables specified in the NAMELIST block, its use is generally restricted to reading the values from a file rather than giving you the flexibility to enter the values interactively. The output capabilities of NAMELIST are limited to every variable in the NAMELIST block or nothing. NAMELIST lacks facilities to selectively output a sublist of variables.

Since many Fortran users are not interested in the programming aspects, per se, but in developing a program that will solve a particular problem, the tedium of programming the UI is not appealing. On the other hand, the UI must be seriously considered for those programs that will be used by anyone other than the programmer.

This article discusses a set of I/O options provided by an application tool called the Fortran Runtime Executive (FORTEX). I developed FORTEX to put a friendly UI on a simulation program that had a decidedly unfriendly interface provided by hardcoded I/O statements. The simulation program designer had previously developed several simulations in ACSL and had become accustomed to its rich set of I/O capabilities. He was proud of the algorithmic code but unhappy with the rigid method of communication with the program. Thus, FORTEX was conceived.

FORTEX is compliant with ANSI Fortran 77 and has been ported to several platforms from PCs to mainframes with very few differences in the code.

FORTEX Features

FORTEX, a runtime environment for Fortran, features a Command-Line Interface (CLI) that uses symbolic commands to control I/O and the order of program operation. It provides facilities for setting and displaying values of the program variables, selectively listing variable values during a run, and the selective recording of these values for post-processing. FORTEX provides a powerful macro feature that allows you to name a block of frequently used commands and invoke these commands by merely entering the macro name. FORTEX will read application command files that can be used to initialize data, define macros, and specify FORTEX system parameters such as the frequency at which data are recorded. FORTEX can also be used to control a purely batch application.

FORTEX provides access to any program variable specified in a labeled common block. Local subroutine and function variables, however, cannot be accessed by FORTEX. A preprocessor parses one or more of the application's common blocks and associated specification statements and builds a subroutine that contains data structures encapsulating all necessary information about the common-block members. This subroutine is used to symbolically access the individual common-block members during the execution of the application program.

Interfacing to the Application Program

FORTEX can be added to an existing program with only a few additions to the code in the form of subroutine calls. Existing I/O statements can be left in the program or removed. Local variables must be placed in common in order to be accessed by FORTEX. You are strongly urged to place each common block and the associated specification statements in a separate include file, but this is not a requirement. The common and specification statements are placed in an input file for the preprocessor in the form of include statements, source statements, or a combination of include and source statements. Include files may contain embedded include statements.

After additions are made to the application program and the preprocessor is executed to build the symbolic access routine, the application code and symbolic access routine are compiled and linked with the FORTEX library routines to build the application program. A makefile is normally used to automate these steps.

The application program is controlled during execution with FORTEX commands; see Table 1. Each command can be abbreviated to three characters, and the frequently used commands SET and DISPLAY can be specified with their first letter. FORTEX considers lowercase to be equivalent to uppercase.

Table 1: FORTEX commands.

  Command    Description
  ------------------------------------------------------------------------

  action     Sets a constant or variable during execution.
  close      Closes a previously opened file.
  contin     Continues execution of the program after an interrupt.
  display    Displays the value of an interface constant or variable.
  dump       Displays the values of all interface constants and variables.
  dynetx     Invokes the graphical user interface (Dynet-X);
              not active for stand-alone FORTEX.
  echo       Prints a message to the display.
  end        Defines the end of a runtime macro.
  exit       Terminates execution of the program (same as "stop").
  help       Invokes the FORTEX help facility.
  history    Lists previous commands.
  include    Loads the contents of a command file (same as "read").
  inquire    Checks whether a Fortran unit number is in use.
  macro      Defines use commands by combining other FORTEX commands.
  open       Opens and alternate, user-supplied, command file or data
              file.
  output     Writes specified variables to the screen during execution.
  plot       Transfers control to a plotting program.
  prepare    Records specified variables for printing or plotting.
  print      Prints recorded variables in tabular form (post-processing).
  quit       Terminates execution of the program (same as "stop").
  range      Finds minimum and maximum values of prepared variables.
  read       Loads the contents of a command file (same as "include").
  reinit     Transfers control to application program to reinitalize
              state variables to current values.
  restore    Restores values previously saved with the save command.
  return     Returns control to the calling routine.
  run        Begins execution of the program (same as "start").
  save       Saves values of the interface constant and variables to a
              binary file.
  set        Sets the value of an interface constant or variable.\
  show       Shows a variable's type, dimension, and location within its
              common block.
  spare      Calls a user-supplied subroutine.
  start      Begins execution of the program (run may also be used).
  stop       Terminates execution of the program (quit or exit may also
              be used).
  xdisplay   Displays the value of an integer constant or variable in
              hexadecimal.
  xset       Set the value of an integer constant or variable in
              hexadecimal.
  !!         Repeat the previous command.
  !an        Repeat the most recent command that begins with the string
              "an."
  !n         Repeat the nth command.
  "comment"  Any text enclosed in quotes is a comment.
  $          Command separator.
  ...        Continuation characters for continuing the present command to
              the next line.

The FORTEX preprocessor builds a subroutine that contains data structures collectively referred to as the FORTEX dictionary. The dictionary contains the variable name, common-block name, location in the common, dimension, and the type for each variable presented as input to the preprocessor. This subroutine allows FORTEX to symbolically access the individual common-block members. The preprocessor also generates an output file containing a special form of each common block input to the preprocessor.

The input file, named clicom.def, contains common-block statements and associated specification statements or include statements that name a file containing common and specification statements. The file clicom.def should contain all common blocks and specification statements required for FORTEX interface access, but it need not contain all commons used in the Fortran application.

The preprocessor builds two output files: clidict.f and clicom.out. clidict.f contains the dictionary subroutine which provides access to the information needed to symbolically address the variables in common blocks. clicom.out contains a special form of each common block parsed by the preprocessor. The common blocks in clicom.out contain only one member for each common block. For noncharacter commons, the single member is a real array with a single dimension equal to the number of single-precision memory words occupied by the common. For character common blocks, the single member is a character variable, the length of which is equal to the total length of all the character members of that common.

Once the interface between the Fortran application and FORTEX is defined by the preprocessor, you can add calls to the FORTEX routines to your code and link with the FORTEX library.

Adding FORTEX to the Application

Adding a FORTEX interface to a Fortran program involves the following steps: Identify UI variables and place them into common blocks; place the common blocks (or include files) in clicom.def for the FORTEX dictionary builder; add FORTEX executive and data-recording calls to the program; execute the preprocessor; compile the application routines and clidict.f; and link the application to the FORTEX library.

The core of the FORTEX interface is a program structure that calls the command-line executive (clexec) and data-recording subroutine (clrecd) at appropriate points. Separating the structure from the application code can be achieved with a simple main program that calls the top-level application subroutine. Calls can be issued from the main program to perform each of the following functions: initialize variables used in the application; run the interface application; continue execution of the interface application following an intermediate termination; and calculate terminal conditions (for example, final values at the end of a FORTEX application run).

The FORTEX preprocessor (clibdict) must be executed before any FORTEX application can be built. clibdict determines (from external files, as previously explained) information about the number, type, size, and order of variables declared in the Fortran application. clibdict then creates a dictionary of storage locations in memory (corresponding to each variable. This dictionary is used by FORTEX to symbolically access the program variables.

All subroutines used with the FORTEX interface can be compiled with a standard Fortran compiler. FORTEX provides a library designed to be linked with application-specific subroutines. This linking process generates an executable file for the interface application.

A Trajectory-analysis Program

Integrating FORTEX with the application is independent of the application-program size. We've used FORTEX with programs having a few variables (less than 25) to very large simulation programs with more than 1000. The integration time depends heavily on the program's use of common blocks. If all variables you wish to access are already in common, and the commons are stored in include files, the integration time may take only a few minutes.

To illustrate, I'll use a trajectory-analysis program called MONTE to show how FORTEX is added to an application and how the application is controlled by the FORTEX commands. The analysis program reads a data file generated by a missile-trajectory program that was repeatedly executed in a Monte Carlo fashion. Monte Carlo refers to a simulation method whereby selected simulation variables are varied for each run. The resulting deviations in the time histories of the missile trajectories indicate the performance boundaries of the missile. The simulation analyst is interested in the expected or average trajectory and in the predicted variations in the trajectories, usually in the form of the 3-standard deviation (3-sigma) trajectory. MONTE computes the average and n sigma trajectories, where n is user selected through FORTEX. A trajectory is composed of the missile downrange, cross-range, and altitude components. The user may select which component to analyze and after analysis is completed on the selected component, another component may be selected.

The main program of an application should normally be a short, executive-type routine that controls the program flow. Listing One (page 116) is the main program. Listing Two (page 116) is the application code, which consists of subroutines dynxrun, mcsig, mcavg, and block-data routine mondat. Listing Three (page 116) is the common-block and associated-specification statements contained in the include file monte.h. The uppercase executable statements in Listings One and Two are subroutine calls that are necessary to invoke the FORTEX CLI and the data-recording routine. This example requires the addition of only two subroutine calls to invoke the FORTEX interface. More sophisticated applications may need additional calls to the data recording routine and to the ACTION routine if runtime actions are desired.

The main program (climain) provides a loop that will repeatedly call the CLI executive (clexec) and the application routines until the user enters the STOP command. The call to clexec invokes the command-line interface which executes FORTEX commands. After a START command is issued, control returns to climain and dynxrun is called to execute the analysis routines. If a STOP command is issued, climain causes the program to terminate.

The call to clrecd is somewhat arbitrary. Generally, it should be placed where data can be recorded for each update of the program with appropriate logic to limit calls as desired by the user at run time. For data that will be plotted, you generally want a higher recording rate than for printed output. FORTEX provides two system parameters to control the amount of output: nciout controls the rate of output as the run executes, and nciprn controls the rate at which post-processing results are printed. The call to clrecd in MONTE is placed so that every computed point is also recorded.

Runtime Session

FORTEX accepts commands from the keyboard during an interactive session, and from a command file during batch execution. Command files are frequently used in interactive sessions as well. Command files are very useful in setting up standard scenarios and for defining customized commands as macros. Included with the electronic version of the listings is the command (or setup) file used with MONTE. The command file uses two macros called LATDAT and VERDAT. LATDAT sets up the parameters to analyze the lateral or cross-range component, and VERDAT sets up the parameters so that the vertical component will be analyzed.

Also available electronically is a partial example runtime session of MONTE. I've placed comments in the (command stream to help explain the commands. After the run is completed, control is passed to the plot program. The resulting trajectory plot for the vertical component is shown in Figure 1.

Conclusion

FORTEX is a Fortran enhancement tool that provides a flexible and easily mastered UI that can be quickly added to an existing program or incorporated into a new application by following a simple set of programming rules. The primary benefit of FORTEX is that the user is put completely in charge of the application program. Any or all variables can be monitored during a run without having to change the code. Traditional "what-if" runs can be made with simple command sequences. Furthermore, FORTEX is useful during the debugging stage since any variable can be monitored without adding print statements.



_FORTEX: A Fortran Runtime Executive_
by Harold R. Justice


[LISTING ONE]
<a name="015e_000c">

c- climain.f -- top-level calling system utilized by FORTEX CLI.
c- The main program calls user routine "dynxrun". For CLI-only, this routine
c- the name is arbitrary. When linked to GUI library, "start" button calls
c- the subroutine "dynxrun", a reserved name that MUST be used with the GUI.
c- Dynetics, Inc. 1000 Explorer Blvd. Huntsville, AL 35806 (205) 922-9230
      program climain
      integer icntl
100   continue
c-------CLEXEC sends the program to the CLI> prompt
        CALL CLEXEC(icntl)
c-------check the return code to see whether the user typed 'stop'
        if( icntl.lt. 0 ) then
          write(*,*)'Normal exit from program'
          stop
        endif
c-------call the user program to do computations, etc.
        call dynxrun
c-----return to the user CLI> prompt so that we may run again
      goto 100
      end







<a name="015e_000d">
<a name="015e_000e">
[LISTING TWO]
<a name="015e_000e">

c- monte.f -- bob graves -- (c) 1992, dynetics, inc., huntsville, al
c- name    type   description
c- ----    ----   -----------
c- dbldat  dp     array to read line of data from file
c- idat    int    data counter
c- irun    int    run counter
c- nmnvec  int    number of points in shortest run
c- nmxvec  int    number of points in longest run
c- numrun  int    number of runs read from file
c- there   log    boolean file exists (t) or not (f)
c- clrecd  sub    FORTEX data recording routine
c- dynxrun sub    main number cruncher for monte program
c- mcavg   sub    compute average for all points, all runs
c- mcsig   sub    compute sigma (std dev) for single point
      subroutine dynxrun
c- include file listed in clicom.def for FORTEX dictionary
      include 'monte.h'
      double precision dbldat(100)
      logical there
c- error checking
       if( nsigma .lt. 0.0 ) then
         write(*,*)'err: nsigma negative: defaults to 3.0'
         nsigma = 3.0
       endif
c- user may access the file name through FORTEX set/display
       inquire( file = fname, exist = there )
       if( .not. there ) then
          write(*, *) 'file not found : ', fname
          return
       endif
c- file exists, so open.
       open( lfdat, file=fname, form='unformatted', status='old')
c- initialize counters.
       idat = 1
       irun = 1
c- data read loop reads from a FORTEX "clirrr" type file and
c- presumes that time (independent variable) has been prepared
c- first on the list. other parameters are data (nparm of them)
100    continue
       read(lfdat,err=110,end=110) t(idat,irun),(dbldat(i),i=1,nparm)
c- pick out the column of data for analysis
     work(idat,irun) = dbldat(ivar)
     if( idat .gt. 1 ) then
c- test whether we have entered into another run.
      if( t(idat, irun ) .lt. t(idat-1,irun) ) then
c- set the number of data points in the run.
        num(irun) = idat - 1
        irun = irun + 1
        if( irun .gt. 1 ) then
c- already into next run, so copy it to first element of next run
          t(1, irun ) = t( idat, irun - 1)
          work(1, irun ) = work( idat, irun - 1)
        endif
        idat = 2
        goto 100
      endif
     endif
     idat = idat + 1
     goto 100
110    continue
c- close the file in case we run again
       close(lfdat)
c- assign the number of data points in the last run.
       num(irun) = idat - 1
       numrun = irun
c- find the length of the shortest and longest runs.
       nmxvec = -1
       nmnvec = mxdat
       do 300 irun = 1, numrun
          nmxvec = max ( num(irun), nmxvec )
          nmnvec = min ( num(irun), nmnvec )
300    continue
c- determine data extend or truncate mode: trunc=.t., truncate long runs;
c-  trunc=.f., extend all shorter runs to be identical to longest run
       if ( trunc) then
     numvec = nmnvec
       else
     numvec = nmxvec
     imxrun = 1
c- load shorter runs with longest run data. mechanism drives std dev to 0.0
     do 350 irun = 1, numrun
      if( nmxvec .eq. num(irun) ) then
         imxrun = irun
      endif
350  continue
c- assign longest run data to shorter run data.
     do 410 idat = nmnvec, nmxvec
       do 400 irun = 1, numrun
           work(idat, irun ) = work(idat, imxrun )
410    continue
400  continue
       endif
c- analysis section. looping for FORTEX output.
c- do average and +/- n sigma computations.
    call mcavg( avgvec, numvec, numrun, mxdat, work )
    do 450 idat = 1, numvec
      avg = avgvec(idat)
      time = t(idat,1)
      call mcsig( avg, idat, numrun, mxdat, work,
   .              nsigma, sigpos, signeg, sig )
c-    FORTEX data recording in clrecd
      CALL CLRECD(idat-1)
  450   continue
       return
       end
       subroutine mcsig( avg, idat, nrun, mxdat, work,
     .           nsigma, ps3, ns3, sig )
c-   compute +/- n sigma for data passed in.
       save
       double precision avg, ps3, ns3, sig, work(mxdat,*), sum
c- initialize.
       sum = 0.0
c- loop through points.
       do 100 irun = 1, nrun
     sum = sum + ( work(idat, irun ) -  avg )**2
100    continue
       if(nrun .gt. 0) then
     sig = sqrt(sum / dble( nrun - 1 ))
       else
     sig = 0.0
       endif
c- compute + and - N sigma vectors.
       ps3 = avg + nsigma * sig
       ns3 = avg - nsigma * sig
       return
       end
       subroutine mcavg( avg, ndat, nrun, mxdat, work )
c- compute the average of nruns of data.
       save
       double precision avg(*), work(mxdat,*), sum
c- initialize.
       do 110 idat = 1, ndat
       sum = 0.0
c- loop the nrun loop.
       do 100 irun = 1, nrun
     sum = sum + work( idat, irun )
100    continue
c- compute the average for this parameter.
       avg( idat ) = sum / dble( nrun )
110    continue
c- return.
       return
       end
      block data mondat
      include 'monte.h'
c- initialize through data statements, may also use the monte.setup file
      data nsigma /3/
      data fname  /'monte.dat'/
      data nparm  /2/
      data ivar   /2/
      end






<a name="015e_000f">
<a name="015e_0010">
[LISTING THREE]
<a name="015e_0010">

c- monte.h / dynetics, inc., huntsville, al
c- name   type  description
c- ----   ----  -----------
c- mxdat  parm  maximum number of data elements per run
c- mxrun  parm  maximum number of runs
c- work   dp    memory copy of raw data for use in statistics
c- avg    dp    average for a given time point
c- avgvec dp    average vector (keep the averages for std dev calc)
c- signeg dp    minus n-sigma value for a given time point
c- sigpos dp    plus n-sigma value for a given time point
c- sig    dp    sigma (std dev) for a given time point
c- nsigma int   1,2,or 3 for the +/- N sigma computation
c- t      dp    time array
c- time   dp    time for a given point (independent variable)
c- nparm  int   number of data columns (not counting time) in file
c- lfdat  int   logical file number of the data
c- fname  char  name of the input data file (binary)
c- num    int   array of number of data points per run
c- ivar   int   which data column to analyze
c- trunc  log   whether to truncate to shortest run (T) or extrapolate
      parameter( mxdat = 1000 )
      parameter( mxrun = 100 )
      double precision work( mxdat, mxrun )
      double precision avg, avgvec(mxdat), signeg, sigpos, sig
      double precision t(mxdat, mxrun ), time
      common /monted/ work, avg, avgvec, signeg, sigpos, sig, time, t
      integer numvar, nsigma, lfdat, nparm, ivar, num(mxrun)
      logical trunc
      common /monte/ numvar, lfdat, nsigma, nparm, ivar, num, trunc
      character*64 fname
      common /montec/ fname



[Example 1:  MONTE command file]

echo Setup for MONTE program
set fname='traj.rrr'
set ivar = 2
set lfdat = 30
set nparm = 2
prepar time,avg,sigpos,signeg,sig
display 'prepare'
macro facts
  echo File name with trajectory data
  display fname
  echo Number of data columns
  display nparm
  echo Column of data to analyze
  display ivar
end
macro sig3
 echo 3 Sigma (nsigma=3)
 set nsigma = 3
end
sig3
macro latdat
  echo Analyze the crossrange data
  set ivar=2
  set trunc=.t.
  set fname='../inmonte/clirrr'
  set nparm=3 $ "number of data columns in missile simulation clirrr file"
  facts
  output 'clear'
  output time,avg,sigpos,signeg
  start
  plot
end
macro verdat
  echo Analyze the altitude data
  set ivar=3
  set trunc=.t.
  set fname='../inmonte/clirrr'
  set nparm=3 $ "number of data columns in missile simulation clirrr file"
  facts
  output 'clear'
  output time,avg,sigpos,signeg
  start
  plot
end
set nciout=10
s cmd=5



[Example 2: Partial output from example run-time session]

riddler77%  DYNET-X (C) 1991 FORTRAN RUN-TIME EXECUTIVE V3.06 DYNETICS, INC.
 CLI> read 'monte.setup'
  Setup for MONTE program
  Prepare List
   TIME          AVG               SIGPOS
   SIGNEG        SIG
  End Prepare List
  3 Sigma (nsigma=3)
 CLI> "Run program to analyze vertical data"
 CLI> display nciout
    NCIOUT 10
 CLI> "Every 10-th data point will be printed to the screen"
 CLI> verdat
  Analyze the altitude data
  File name with trajectory data
 FNAME '../inmonte/clirrr                                               '
  Number of data columns
     NPARM 3
  Column of data to analyze
      IVAR 3
      TIME 0.00000000        AVG 0.00000000      SIGPOS 0.00000000
      SIGNEG 0.00000000
      TIME 0.09999999        AVG 18.96214371         SIGPOS 20.76645258
      SIGNEG 17.15783483
      TIME 0.20000002        AVG 32.36762810         SIGPOS 36.44571089
      SIGNEG 28.28954530
         .
         .
         .
      TIME 4.80001497        AVG 719.5072083         SIGPOS 784.4273663
      SIGNEG 654.5870502
      TIME 4.90001726        AVG 738.4712952         SIGPOS 805.1422782
      SIGNEG 671.8003122
 CLI> "Only output every 100th point"
 CLI> set nciout=100
 CLI> "Run program to analyze the lateral data"
 CLI> latdat
  Analyze the crossrange data
  File name with trajectory data
 FNAME '../inmonte/clirrr                                               '
  Number of data columns
     NPARM 3
  Column of data to analyze
      IVAR 2
      TIME 0.00000000        AVG 0.00000000      SIGPOS 0.00000000
      SIGNEG 0.00000000
      TIME 0.99999934        AVG 13.08728914         SIGPOS 15.78440254
      SIGNEG 10.39017575
      TIME 1.99999845        AVG 25.58025684         SIGPOS 28.22318408
      SIGNEG 22.93732961
      TIME 2.99999762        AVG 37.22265167         SIGPOS 40.50114335
      SIGNEG 33.94416000
      TIME 3.99999666        AVG 50.43453903         SIGPOS 54.86466342
      SIGNEG 46.00441465
 CLI> display nsigma
    NSIGMA 3
 CLI> "Change sigma level to 2"
 CLI> set nsigma = 2
 CLI> "Run lateral case again to obtain 2-sigma trajectories"
 CLI> "Normally, you would type 'start' to run the program"
 CLI> "but we would like to illustrate the repeat command"
 CLI> !20
       latdat
  Analyze the crossrange data
  File name with trajectory data
 FNAME '../inmonte/clirrr                                               '
  Number of data columns
     NPARM 3
  Column of data to analyze
      IVAR 2
      TIME 0.00000000        AVG 0.00000000      SIGPOS 0.00000000
      SIGNEG 0.00000000
      TIME 0.99999934        AVG 13.08728914         SIGPOS 14.88536474
      SIGNEG 11.28921355
      TIME 1.99999845        AVG 25.58025684         SIGPOS 27.34220833
      SIGNEG 23.81830535
      TIME 2.99999762        AVG 37.22265167         SIGPOS 39.40831279
      SIGNEG 35.03699056
      TIME 3.99999666        AVG 50.43453903         SIGPOS 53.38795529
      SIGNEG 47.48112278
 CLI> stop


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.