home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Journal 1990 - 1995 / CUJ.iso / unix / 1994.txt < prev    next >
Encoding:
Text File  |  1996-02-07  |  2.9 MB  |  88,156 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173.  
  174.  
  175.  
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.  
  206.  
  207.  
  208.  
  209.  
  210.  
  211.  
  212.  
  213.  
  214.  
  215.  
  216.  
  217.  
  218.  
  219.  
  220.  
  221.  
  222.  
  223.  
  224.  
  225.  
  226.  
  227.  
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238.  
  239.  
  240.  
  241.  
  242.  
  243.  
  244.  
  245.  
  246.  
  247.  
  248.  
  249.  
  250.  
  251.  
  252.  
  253.  
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.  
  317.  
  318.  
  319.  
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370.  
  371.  
  372.  
  373.  
  374.  
  375.  
  376.  
  377.  
  378.  
  379.  
  380.  
  381.  
  382.  
  383.  
  384.  
  385.  
  386.  
  387.  
  388.  
  389.  
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.  
  404.  
  405.  
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438.  
  439.  
  440.  
  441.  
  442.  
  443.  
  444.  
  445.  
  446.  
  447.  
  448.  
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469.  
  470.  
  471.  
  472.  
  473.  
  474.  
  475.  
  476.  
  477.  
  478.  
  479.  
  480.  
  481.  
  482.  
  483.  
  484.  
  485.  
  486.  
  487.  
  488.  
  489.  
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496.  
  497.  
  498.  
  499.  
  500.  
  501.  
  502.  
  503.  
  504.  
  505.  
  506.  
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.  
  602.  
  603.  
  604.  
  605.  
  606.  
  607.  
  608.  
  609.  
  610.  
  611.  
  612.  
  613. A Short Floating- Point Type in C++
  614.  
  615.  
  616. William Smith
  617.  
  618.  
  619. William Smith is the engineering manager at Montana Software, a software
  620. development company specializing in custom applications for MS- DOS and
  621. Windows. You may contact him by mail at P.O. Box 663, Bozeman, MT 59771- 0663.
  622.  
  623.  
  624.  
  625.  
  626. Introduction
  627.  
  628.  
  629. Even though a typical microcomputer can have up to ten times the memory of one
  630. just a few years ago, there are still programming problems where memory is a
  631. limiting factor. I frequently bump into memory limitations in embedded and
  632. data acquisition applications. Numerous times I have had to work with a large
  633. quantity of floating- point numbers in a confining space. A common situation
  634. is the acquisition of large amounts of data through a 14- bit (or smaller) A-
  635. to- D (Analog to Digital) converter.
  636. Storing these numbers as 32-bit floats always seemed like overkill to me and a
  637. waste of space. This was especially annoying when I had to store tens of
  638. thousands of points in an array and would hit some kind of a memory limitation
  639. such as a segment boundary, physical memory limit, or even a file or disk size
  640. limit. The standard float type works, but it represents a poor match to the
  641. problem to be solved. Matching the floating- point size to what an application
  642. needs can result in significant memory savings in data- intensive programs.
  643. I really only needed a 16-bit floating- point type instead of the native
  644. 32-bit float. At first, I played some games and stored the data as short int.
  645. But this forced me to convert the data to float to do anything useful with it.
  646. I wanted a short floating-point type. I even implemented one, albeit crudely,
  647. in C. C allowed me to do it, but the conversion process never was clean or
  648. transparent. With C++, I was finally able to do what I wanted. I was able to
  649. create a short floating-point type that I could use naturally in my
  650. applications. C++ can hide all the dirty work, such as conversions.
  651. The new type, which I call sfloat, even allowed me to control range and
  652. precision. Some situations called for a floating-point type that ranged
  653. between 0 and 10.0 and maximized the precision within that range. Other
  654. situations required a larger signed range but less precision. Being able to
  655. tailor the characteristics of the type to meet an application's needs was a
  656. practical feature I built into sfloat,
  657. I implemented the sfloat type in "Standard C++" (if there is such a beast).
  658. The code works with Microsoft C++ and Borland C++ under MS-DOS and MS-Windows.
  659. It has some dependencies on the size of the standard types float, unsigned
  660. short int, and long. It assumes that:
  661. a float is 32 bits
  662. an unsigned short int is 16 bits
  663. a long is 32 bits
  664. It also assumes that the float type is that defined by the IEEE standard for
  665. 32-bit floating-point values. Table 1 gives the IEEE details. As long as a
  666. compiler and operating system conform to these restrictions, the code for
  667. sfloat will probably work in other environments.
  668.  
  669.  
  670. Implementation
  671.  
  672.  
  673. Listing 1, sfloat.hpp, defines a C++ class called sfloat. The class has
  674. numerous private static members, one protected member and numerous public
  675. member functions. There are even some non-member functions prototyped in
  676. sfloat.hpp.
  677. The static data provides a workspace for conversion between sfloat and float.
  678. This static data is class specific. All instances, or objects, of class sfloat
  679. share the same static data. The protected member s is the only object instance
  680. data. This member is unique to each instance of sfloat. In fact, the sizeof
  681. operator will report the size of sfloat to be the size of this member, 2
  682. bytes.
  683.  
  684.  
  685. Constructors
  686.  
  687.  
  688. One of the most elemental functions for a C++ class is the constructor. A
  689. constructor has the same function name as for the class. Furthermore, you can
  690. overload the constructor to provide construction from (conversion from)
  691. different types. The sfloat class has three constructors.
  692. sfloat();
  693. sfloat(float f);
  694. sfloat(sfloat& sf);
  695. sfloat() defines the "default" construction of an sfloat object, such as on
  696. the stack. The compiler would generate this function automatically if you do
  697. not specify it. sfloat(float f) converts a floating-point number to an sfloat
  698. to initalize the stored value. sfloat(sfloat& sf) initializes the new object
  699. by making a copy of another sfloat object. These three constructors provide
  700. the functionality needed to support the following declarations using sfloat.
  701. sfloat sf1;
  702. // uses sfloat();
  703. sfloat sf2 = 1.0f;
  704. // uses sfloat(float f);
  705. sfloat sf3 = sf2;
  706. // uses sfloat(sfloat& sf);
  707. These three types of construction and initialization cover the minimum
  708. required to use sfloat type naturally. The code for the constructor functions
  709. resides in Listing 2, sfloat.inl. sfloat() and sfloat(sfloat& sf) are very
  710. simple. On the other hand, sfloat(float f) has to do a bit of work. It has to
  711. convert a float to an unsigned short and assign it to the object instance data
  712. member s.
  713. The conversion process used in sfloat(float f) truncates the mantissa bits to
  714. a lower precision. It also lowers the range of the exponent by discarding
  715. higher-order bits. The conversion process utilizes some of the static data
  716. members of class sfloat as a work space and to hold intermediate values. The
  717. bitwise shift operators << and >> move the bits that will be kept from the
  718. float value into place before they are packed into an unsigned short.
  719. Since none of the constructor functions allocate memory on the heap (free
  720. store) using new there is no need to define a destructor function. C++ will
  721. provide a default destructor that does nothing.
  722.  
  723.  
  724. Conversion to float
  725.  
  726.  
  727. We also need a way to convert an sfloat object to a float. To use conventional
  728. notation, we need to define the operator function
  729. sfloat::operator float()
  730. Listing 2, sfloat. inl, contains the definition of this function. You will
  731. notice that it's logic is just the reverse of sfloat:: sfloat(float f). The
  732. shift operators once again move the bits of the sfloat into the proper
  733. locations in the 32 bits of a float. The extra bits are filled with zeros.
  734.  
  735.  
  736.  
  737. Overloaded Operators
  738.  
  739.  
  740. Operator overloading is one of the features of C++ that allow you to use new
  741. defined types just like the standard existing types. Operator overloading is
  742. not so much an object-oriented feature as a convenience. Table 2, an extract
  743. from Listing 1, lists the operator functions defined for sfloat. This list
  744. includes all the operators that one commonly uses on floating-point numbers.
  745. These operator functions allow you to use objects of the class sfloat just
  746. like you would a standard floating-point type.
  747. Operator overloading is fairly straight-forward feature of C++ and covered
  748. well elsewhere. I recommend the "Stepping Up To C++" series of articles on
  749. "Operator Overloading" by Dan Saks (see CUJ January, March, May, and July
  750. 1992). I took a very simple approach to implementing these operators. I
  751. convert to float, use the predefined operations, then convert back to sfloat.
  752. For example, here is the code for the add-assignment operator:
  753. inline sfloat &sfloat::
  754. operator+=(sfloat sf)
  755. {
  756. float f = (float)*this;
  757. f += (float)sf;
  758. *this = (sfloat)f;
  759. return ( *this );
  760. } // operator+=
  761. This techniques is not the most efficient (it has to do three type
  762. conversions), but it sure is simple. My needs for the sfloat type were
  763. data-size driven, not code-speed or code-size driven. Consequently I can live
  764. with the overhead of all those conversions. If you cannot, you could rewrite
  765. some of these routines to operate directly on the sfloat type.
  766. I would like to emphasize that you can get trapped into inefficiency with
  767. operator overloading. If you are not careful, your operator overloading can
  768. force unneeded object construction and destruction, especially for the
  769. operators +, - , *, and /. One trick to avoid this is to use the corresponding
  770. assignment operators (such as +=) with a reference return type to define the
  771. other math operators. This technique results in the interesting side effect
  772. that the operators +, - , *, and / are neither member or friend functions.
  773.  
  774.  
  775. Inlining
  776.  
  777.  
  778. In implementing the sfloat class, I choose to inline the overloaded operator
  779. functions and the conversion functions. Inlining a function means that its
  780. code gets inserted into your compiled program each time the function is
  781. called. This can cause your program to bloat in size unexpectedly. If you find
  782. this happening, I recommend you do not inline at least the two conversion
  783. functions sfloat::sfloat(float f) and sfloat::operator float(). Both are
  784. fairly long. But experiment first. To discontinue inlining for a function,
  785. remove the inline modifier from its function definition and move the
  786. definition from the file sfloat. inl, (Listing 2) to sfloat.cpp (Listing 3).
  787. Some of operator functions are very short. You may wonder why I did not
  788. include their definitions with the class definition in the file sfloat.hpp.
  789. Instead I grouped all the inline functions in the file sfloat. inl. This is
  790. not quite standard, but I have to agree with Walter Bright, one of the C++
  791. compiler pioneers. Inline function bodies appearing in the class body clutters
  792. the class definition (C++ Report October 1992).
  793. Including inline functions with the class definition also violates the
  794. separation of the implementation of a class function members from the class
  795. definition. For maintenance purposes, it is a good technique to isolate the
  796. two. The class definition is the class interface and should change less than
  797. the member function implementation.
  798.  
  799.  
  800. Controlling Range and Precision
  801.  
  802.  
  803. The function sfloatrange, Listing 3 (slfoat.cpp), provides a way to adjust the
  804. range, signedness, and precision of the sfloat type:
  805. friend void sfloatrange(
  806. unsigned short sfNumExpBits,
  807. unsigned short sfSigned);
  808. The first parameter is the number of exponent bits. This can be any number
  809. from 1 to 8. The higher the number the larger the range of values that sfloat
  810. can represent. Eight bits is the same as for the standard float type. Table 3
  811. shows the maximum value that sfloat can represent for each of the possible
  812. numbers of exponent bits.
  813. The second parameter determines whether or not sfloat is a signed value. If
  814. the value is signed, sfloat reserves one of its bits as a sign bit. The number
  815. of mantissa bits is the remaining bits out of 16 not used by the exponent or
  816. the sign. That number can range from a minimum of seven to a maximum of 15:
  817. The minimum of 7 results from specifying eight exponent bits and designating
  818. sfloat as signed.
  819. The maximum of 15 results from specifying one exponent bit and designating
  820. sfloat as unsigned.
  821. Table 4 lists all the possible numbers of mantissa bits and the corresponding
  822. (minimum) number of significant decimal digits.
  823. I have encountered a requirement to have an unsigned floating-point
  824. representation that needs only four significant digits and a range of four
  825. orders of magnitude (0 to 104). A combination of 11 mantissa bits, five
  826. exponent bits and no sign bit worked fine.
  827. The defaults, if you do not call sfloatrange, are eight exponent bits, a sign
  828. bit, and seven mantissa bits. This yields the same range as the standard
  829. float, but with much less precision. These values make the conversions between
  830. sfloat and float particularly easy. You can just use a union of a float and
  831. two unsigned shorts. To convert from a float, just store in the float member
  832. of the union and extract the second unsigned short. To convert from an sfloat,
  833. you reverse the process. Notice that the conversion functions do this for the
  834. special default range situation.
  835. There are limitations with range setting. The sfloat class uses static data to
  836. preserve the range information. This prevents you from tailoring the range
  837. individually for each instance (object) of the class. In other words, once you
  838. set the range, all sfloat objects have the same range. You could have each
  839. instance retain information about the number of exponent, mantissa, and sign
  840. bits, but this would require each object to store information about range and
  841. defeat the desire to save space. Use of static data also helps to speed up
  842. conversions.
  843. Use of static data to store the range information has repercussions in
  844. multitasking or multithreaded environments. Static data prevents the code for
  845. sfloat from being re-entrant. You cannot preserve different range information
  846. between tasks if the tasks are sharing the same code such as a Windows DLL
  847. (Dynamic Link Library).
  848. To keep sfloat small and make it re-entrant would require eliminating the size
  849. adjustability. This would force you to create a different class for each of
  850. the different range combinations used. Some real time or multitasking
  851. situations may demand you eliminate the range adjustability.
  852.  
  853.  
  854. Conclusions
  855.  
  856.  
  857. Some of the basic features of C++ make the solution to specific problems
  858. elegant and easy compared to C. I have presented a short floating-point type
  859. sfloat that utilizes operator overloading for notational convenience. You can
  860. easily integrate this new type into your C++ applications. The sfloat type is
  861. a 16-bit (two-byte) floating-point representation that you can use instead of
  862. the standard four-byte float.
  863. The sfloat type has appeal in applications that need only 16 bits for a
  864. floating-point type and require the storage of large amount of data. If you
  865. have particular requirements on range, precision, and signedness, you can
  866. tailor this type to best match your needs. In this way, you can get as many as
  867. five significant decimal digits (only one less than the standard float) in the
  868. range 0.0 to 2.0. You can also trade precision for range to get the same range
  869. as the standard float but with only three significant digits.
  870. Table 1 IEEE 32-bit float format
  871. Bits Meaning
  872. --------------------------------------------------------------------
  873. 0-21 23- bit mantissa between 1.0 and 2.0 (high-order bit implied)
  874. 22-30 eight-bit exponent (excess 127 binary exponent)
  875. 31 sign bit
  876. Table 2 Overloaded Operators for sfloat
  877. Member function assignment operators
  878.  
  879. --------------------------------------------------
  880. sfloat &operator+=( sfloat sf );
  881. sfloat &operator-=( sfloat sf );
  882. sfloat &operator*=( sfloat sf );
  883. sfloat &operator/=( sfloat sf );
  884.  
  885. Member function unary operators
  886. --------------------------------------------------
  887. sfloat operator+();
  888. sfloat operator-();
  889. sfloat operator++();
  890. sfloat operator--();
  891. sfloat operator++( int );
  892. sfloat operator--( int );
  893.  
  894. Friend function relational operators
  895. --------------------------------------------------
  896. friend int operator==( sfloat sf1, sfloat sf2 );
  897. friend int operator=( sfloat sf1, sfloat sf2 );
  898. friend int operator( sfloat sf1, sfloat sf2 );
  899. friend int operator>( sfloat sf1, sfloat sf2 );
  900. friend int operator<( sfloat sf1, sfloat sf2 );
  901. friend int operator!=( sfloat sf1, sfloat sf2 );
  902.  
  903. Non-member, non-friend function math operators
  904. --------------------------------------------------
  905. sfloat operator+( sfloat sf1, sfloat sf2 );
  906. sfloat operator-( sfloat sf1, sfloat sf2 );
  907. sfloat operator*( sfloat sf1, sfloat sf2 );
  908. sfloat operator/( sfloat sf1, sfloat sf2 );
  909. Table 3 Exponent size and range
  910. Exponent bits Maximum Value
  911. ----------------------------
  912. 1 2
  913. 2 4
  914. 3 16
  915. 4 256
  916. 5 65,536
  917. 6 4.29*109
  918. 7 1.84*1019
  919. 8 3.40*1038
  920. Table 4 Mantissa size and number of significant digits
  921. Mantissa bits Significant Digits
  922. ---------------------------------
  923. 7 2
  924. 8 3
  925. 9 3
  926. 10 3
  927. 11 4
  928. 12 4
  929. 13 4
  930. 14 4
  931. 15 5
  932.  
  933. Listing 1 Definition of class sfloat
  934. #if !defined ( SFLOAT_DEFINED )
  935. #define SFLOAT_DEFINED
  936.  
  937. union conv
  938.  
  939. {
  940. float f;
  941. long l;
  942. unsigned short s[2];
  943. };
  944.  
  945. class sfloat
  946. {
  947.  
  948. private:
  949. // class data
  950. static unsigned long fManSignMask;
  951. static unsigned long fExpMask;
  952. static unsigned long fManMask;
  953. static unsigned short sfManSignMask;
  954. static unsigned short ManSign;
  955. static unsigned short Exp;
  956. static unsigned short Man;
  957. static unsigned short fBias;
  958. static unsigned short sfBias;
  959. static unsigned short fsfBias;
  960. static unsigned short Signed;
  961. static unsigned short fExpBits;
  962. static unsigned short sfExpBits;
  963. static unsigned short fManBits;
  964. static unsigned short sfManBits;
  965. static unsigned short fManSignBits;
  966. static unsigned short sfManSignBits;
  967. static unsigned short sfBits;
  968. static unsigned short fManShift1;
  969. static unsigned short fManShift2;
  970. static unsigned short sfManShift;
  971. static unsigned short fExpShift;
  972. static unsigned short sfExpShift;
  973. static unsigned short sfExpBitsMin;
  974. static unsigned short sfExpBitsMax;
  975. static union conv u;
  976.  
  977. protected:
  978. // object instance data
  979. unsigned short s;
  980.  
  981. public:
  982. // constructors
  983. sfloat();
  984. sfloat( float );
  985. sfloat( sfloat& sf );
  986.  
  987. // conversion
  988. operator float();
  989.  
  990. // member function assignment operators
  991. sfloat &operator+=( sfloat sf );
  992. sfloat &operator-=( sfloat sf );
  993. sfloat &operator*=( sfloat sf );
  994. sfloat &operator/=( sfloat sf );
  995.  
  996. // member function unary operators
  997. sfloat operator+();
  998.  
  999. sfloat operator-();
  1000. sfloat operator++();
  1001. sfloat operator--();
  1002. sfloat operator++( int );
  1003. sfloat operator--( int );
  1004.  
  1005. // friend function relational operators
  1006. friend int operator==( sfloat sf1, sfloat sf2 );
  1007. friend int operator>=( sfloat sf1, sfloat sf2 );
  1008. friend int operator<=( sfloat sf1, sfloat sf2 );
  1009. friend int operator>( sfloat sf1, sfloat sf2 );
  1010. friend int operator<( sfloat sf1, sfloat sf2 );
  1011. friend int operator!=( sfloat sf1, sfloat sf2 );
  1012.  
  1013. // utility function
  1014. friend void sfloatrange(
  1015. unsigned short sfNumExpBits,
  1016. unsigned short sfSigned );
  1017.  
  1018. }; // class sfloat
  1019.  
  1020. // non-member function math operators
  1021. sfloat operator+( sfloat sf1, sfloat sf2 );
  1022. sfloat operator-( sfloat sf1, sfloat sf2 );
  1023. sfloat operator*( sfloat sf1, sfloat sf2 );
  1024. sfloat operator/( sfloat sf1, sfloat sf2 );
  1025.  
  1026. #include <sfloat.inl>
  1027.  
  1028. #endif
  1029.  
  1030. // End of SFLOAT.HPP
  1031.  
  1032.  
  1033. Listing 2 Constructor functions and overloaded operators
  1034. inline sfloat::sfloat() {}
  1035.  
  1036. inline sfloat::sfloat( float f )
  1037. {
  1038.  
  1039. // Init conversion union
  1040. u.f = f;
  1041.  
  1042. // Get the sign
  1043. if ( Signed )
  1044. {
  1045. if ( !fsfBias )
  1046. {
  1047. s = u.s[1];
  1048. return;
  1049. }
  1050. s = (unsigned short)
  1051. (( u.l & fManSignMask ) >> sfBits );
  1052. }
  1053. else
  1054. {
  1055. s = 0;
  1056. }
  1057.  
  1058.  
  1059. // Get the exponent
  1060. Exp = (unsigned short)
  1061. (( u.l & fExpMask ) >> fManBits ) -
  1062. fsfBias;
  1063.  
  1064. // Compress the exponent
  1065. Exp = ( Exp << ( sfExpShift )) >> sfManSignBits;
  1066.  
  1067. // Get the mantissa
  1068. Man = (unsigned short)(( u.l & fManMask ) >>
  1069. ( sfManShift ));
  1070.  
  1071. s = Man Exp;
  1072.  
  1073. } // sfloat( float )
  1074.  
  1075. inline sfloat::sfloat( sfloat& sf ) : s( sf.s ) {}
  1076.  
  1077. // Cast - conversion operators
  1078. inline sfloat::operator float()
  1079. {
  1080.  
  1081. // Get the sign - Init conversion union
  1082. if ( Signed )
  1083.  
  1084. {
  1085. if ( !fsfBias )
  1086. {j
  1087. u.s.[1] = s;
  1088. return ( u.f );
  1089. }
  1090. u.l = ( (unsigned long)
  1091. (s & sfManSignMask )) << sfBits;
  1092. }
  1093. else
  1094. {
  1095. u.l = 0L;
  1096. }
  1097.  
  1098. // Get exponent
  1099. u.l = (unsigned long)((( s << sfManSignBits ) >>
  1100. fExpShift ) + fsfBias ) << fManBits;
  1101.  
  1102. // Get the mantisa
  1103. u.l = ( (unsigned long)( s << fManShift1 )) <<
  1104. fManShift2;
  1105.  
  1106. return ( u.f );
  1107.  
  1108. } // operator(float)
  1109.  
  1110. // Overloaded operators
  1111.  
  1112. // Assigment operators
  1113. inline sfloat &sfloat::operator+=( sfloat sf )
  1114. {
  1115. float f = (float)*this;
  1116. f += (float)sf;
  1117. *this = (sfloat)f;
  1118.  
  1119. return ( *this );
  1120. } // operator+=
  1121.  
  1122. inline sfloat &sfloat::operator-=( sfloat sf )
  1123. {
  1124. float f = (float)*this;
  1125. f - = (float)sf;
  1126. *this = (sfloat)f;
  1127. return ( *this );
  1128. } // operator-=
  1129.  
  1130. inline sfloat &sfloat::operator*=( sfloat sf )
  1131. {
  1132. float f = (float)*this;
  1133. f *= (float)sf;
  1134. *this = (sfloat)f;
  1135. return ( *this );
  1136. } // operator*=
  1137.  
  1138. inline sfloat &sfloat::operator/=( sfloat sf )
  1139. {
  1140. float f = (float)*this;j
  1141. *this = (sfloat)f;
  1142. return ( *this );
  1143. } // operator*=
  1144.  
  1145. // increment operators
  1146. inline sfloat sfloat::operator++()
  1147. { return ( *this += 1.0f ); }
  1148.  
  1149. inline sfloat sfloat::operator--()
  1150. { return ( *this - = 1.0f ); }
  1151.  
  1152. inline sfloat sfloat::operator++( int )
  1153. {
  1154. sfloat sf( *this );
  1155. *this += 1.0f;
  1156. return ( sf );
  1157. } // operator ++
  1158.  
  1159. inline sfloat sfloat::operator--(int )
  1160. {
  1161. sfloat sf( *this );
  1162. *this - = 1.0f;
  1163. return ( sf );
  1164. } // operator --
  1165.  
  1166. // sign change operators
  1167. inline sfloat sfloat::operator+()
  1168. { return ( *this ); }
  1169.  
  1170. inline sfloat sfloat::operator-()
  1171. {
  1172. sfloat sf( 0.0f ):
  1173. return ( sf - *this );
  1174. } // operator -
  1175.  
  1176. // Logical operators
  1177. inline int operator==( sfloat sf1, sfloat sf2)
  1178.  
  1179. { return ( sf1.s == sf2.s ); }
  1180.  
  1181. inline int operator<=( sfloat sf1, sfloat sf2 )
  1182. { return ( sf1.s <= sf2.s ); }
  1183.  
  1184. inline int operator>=( sfloat sf1, sfloat sf2 )
  1185. { return ( sf1.s >= sf2.s ); }
  1186.  
  1187. inline int operator<( sfloat sf1, sfloat sf2 )
  1188. { return ( sf1.s < sf2.s ); }
  1189.  
  1190. inline int operator>( sfloat sf1, sfloat sf2 )
  1191. { return ( sf1.s > sf2.s ); }
  1192.  
  1193. inline int operator!=( sfloat sf1, sfloat sf2 )
  1194. { return ( !( sf1.s == sf2.s )); }j
  1195. // math operations
  1196. inline sfloat operator+( sfloat sf1, sfloat sf2 )
  1197. { return ( sf1 += sf2 ); }
  1198.  
  1199. inline sfloat operator-( sfloat sf1, sfloat sf2 )
  1200. { return ( sf1 - = sf2 ); }
  1201.  
  1202. inline sfloat operator*( sfloat sf1, sfloat sf2 )
  1203. { return ( sf1 *= sf2 ); }
  1204.  
  1205. inline sfloat operator/( sfloat sf1, sfloat sf2 )
  1206. { return ( sf1 /= sf2 ); }
  1207.  
  1208. // End of SFLOAT.INL
  1209.  
  1210.  
  1211. Listing 3 Definition of default values and function sfloatrange
  1212. #include <sfloat.hpp>
  1213.  
  1214. // Work space
  1215. unsigned short sfloat::Exp;
  1216. unsigned short sfloat::Man;
  1217.  
  1218. // Bit masks to extract parts of ieee float
  1219. unsigned long sfloat::fManSignMask = 0x80000000L;
  1220. unsigned long sfloat::fExpMask = 0x7F800000L;
  1221. unsigned long sfloat::fManMask = 0x007FFFFFL;
  1222.  
  1223. // Bit mask to extract parts of short float
  1224. unsigned short sfloat::sfManSignMask = 0x8000;
  1225.  
  1226. // float exponent bias
  1227. unsigned short sfloat::fBias = 127;
  1228.  
  1229. // short float exponent bias
  1230. unsigned short sfloat::sfBias = 127;
  1231.  
  1232. // fBias - sfBias
  1233. unsigned short sfloat::fsfBias= 0;
  1234.  
  1235. // if signed flag
  1236. unsigned short sfloat::Signed = 1;
  1237.  
  1238.  
  1239. // number of float exponent bits
  1240. unsigned short sfloat::fExpBits = 8;
  1241.  
  1242. // number of short float exponent bits
  1243. unsigned short sfloat::sfExpBits = 8;
  1244.  
  1245. // number of float mantissa bits
  1246. unsigned short sfloat::fManBits = 23;
  1247.  
  1248. // number of short float mantiss bits
  1249. unsigned short sfloat::sfManBits = 7;
  1250.  
  1251. // number of float mantissa sign bits
  1252. unsigned short sfloat::fManSignBits = 1;
  1253.  
  1254. // number of float mantissa sign bits
  1255. unsigned short sfloat::sfManSignBits = 1;
  1256.  
  1257. // number of short float bits
  1258. unsigned short sfloat::sfBits = 16;
  1259.  
  1260. // float mantissa shift
  1261. unsigned short sfloat::fManShift1 = 9;
  1262.  
  1263. // float mantissa shift
  1264. unsigned short sfloat::fManShift2 = 7;
  1265.  
  1266. // short float mantissa shift
  1267. unsigned short sfloat::sfManShift = 16;
  1268.  
  1269. // float exponent shift
  1270. unsigned short sfloat::fExpShift = 8;
  1271.  
  1272. // short float exponent shift
  1273. unsigned short sfloat::sfExpShift = 8;
  1274.  
  1275. // short float exponent minimum bits
  1276. unsigned short sfloat::sfExpBitsMin = 1;
  1277.  
  1278. // short float exponent maximum bits
  1279. unsigned short sfloat::sfExpBitsMax = 8;
  1280.  
  1281. // union for conversion
  1282. union conv sfloat::u;
  1283.  
  1284. void sfloatrange( unsigned short sfNumExpBits,
  1285. unsigned short sfSigned )
  1286. {
  1287.  
  1288. // Set the number of short float exponent bits
  1289. sfloat::sfExpBits = sfNumExpBits;
  1290. if ( sfloat::sfExpBits > sfloat::sfExpBitsMax )
  1291. {
  1292. sfloat::sfExpBits = sfloat::sfExpBitsMax;
  1293. }
  1294. else if ( sfloat::sfExpBits <
  1295. sfloat::sfExpBitsMin )
  1296. {
  1297. sfloat::sfExpBits = sfloat::sfExpBitsMin;
  1298.  
  1299. }
  1300.  
  1301. // Set the number of short float sign bits
  1302. if ( sfSigned )
  1303. {
  1304. sfloat::Signed = sfloat::sfManSignBits = 1;
  1305. }
  1306. else
  1307. {
  1308. sfloat::Signed = sfloat::sfManSignBits = 0;
  1309. }
  1310.  
  1311. // Set the number of short float mantissa bits
  1312. sfloat::sfManBits = sfloat::sfBits -
  1313. sfloat::sfManSignBits - sfloat::sfExpBits;
  1314.  
  1315. // Set the short float exponent bias value
  1316. sfloat::sfBias = 0;
  1317. sfloat::sfBias =
  1318. ( 1 << ( sfloat::sfExpBits - 1 )) - 1;
  1319. sfloat::fsfBias = sfloat::fBias - sfloat::sfBias;
  1320.  
  1321. // Set the converson shift values
  1322. sfloat::fManShift1 = sfloat::sfManSignBits +
  1323. sfloat::sfExpBits;
  1324. sfloat::fManShift2 = sfloat::sfBits -
  1325. sfloat::fExpBits - sfloat::fManSignBits;
  1326. sfloat::sfManShift = sfloat::fManBits -
  1327. sfloat::sfBits + sfloat::sfExpBits +
  1328. sfloat::sfManSignBits;
  1329. sfloat::fExpShift = sfloat::sfManBits +
  1330. sfloat::sfManSignBits;
  1331. sfloat::sfExpShift = sfloat::sfBits -
  1332. sfloat::sfExpBits;
  1333.  
  1334. } // sfloatrange
  1335.  
  1336. // End SFLOAT.CPP
  1337.  
  1338.  
  1339.  
  1340.  
  1341.  
  1342.  
  1343.  
  1344.  
  1345.  
  1346.  
  1347.  
  1348.  
  1349.  
  1350.  
  1351.  
  1352.  
  1353.  
  1354.  
  1355.  
  1356.  
  1357.  
  1358.  
  1359.  
  1360.  
  1361.  
  1362. The Annotated ANSI C Standard
  1363.  
  1364.  
  1365. P.J. Plauger
  1366.  
  1367.  
  1368. P.J. Plauger is senior editor of The C Users Journal. He is convenor of the
  1369. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  1370. latest books are The Standard C Library, and Programming on Purpose (three
  1371. volumes), all published by Prentice-Hall. You can reach him at
  1372. pjp@plauger.com.
  1373.  
  1374.  
  1375. If you really take your portable C seriously, there's no substitute for a
  1376. handy copy of the C Standard. It is, by definition, the last word on what's
  1377. valid Standard C and what's not. Unfortunately, you have to shell out roughly
  1378. $65 or more (last time I asked) to either ANSI in New York City or ISO in
  1379. Geneva to get a copy. And what you get for your money is a couple hundred
  1380. pages of dense legalese. (We who wrote the C Standard also provided a
  1381. Rationale, but that got dropped somewhere along the road to approval.)
  1382. When I wrote The Standard C Library (Prentice Hall, 1992), I had the devil's
  1383. own time getting permission to reprint about half the C Standard. ANSI has
  1384. been struggling for years to work out a reasonable policy for granting reprint
  1385. rights to their programming language standards, which have been growing
  1386. steadily and rapidly in commercial importance. They just plain didn't have
  1387. their act together when I asked. ISO was a bit better off, but still ill
  1388. prepared to deal with the more complex standards that we programmers care
  1389. about. Eventually, I got my permission from ISO, but I'm still not sure how.
  1390. Now it seems that Osborne McGraw-Hill has also managed to strike a reprint
  1391. deal, this time with ANSI. They offer this complete and verbatim edition of
  1392. the ANSI/ISO C Standard for a mere $39.95 list price. Not only that, they
  1393. provide considerable running commentary as well. The latter takes the form of
  1394. annotation supplied by Herbert Schildt, one of the more prolific authors in
  1395. the realm of C and C++ "how 2" books.
  1396. You run a slight risk in using a derived standard such as this one. I had to
  1397. convert Dave Prosser's troff master into a form digestible by Ventura
  1398. Publisher. In the process, I introduced a number of typographical errors,
  1399. mostly trivial formatting botches. A cursory scan of Osborne's opus has so far
  1400. turned up only one typo. Far worse, however is the duplication of page 131 in
  1401. place of page 132, which blows a nasty whole in the middle of fprintf. Those
  1402. two botches make a prima facie case that the reproduction is not assuredly
  1403. perfect. Still, they seem to have preserved the pagination of the official
  1404. standard, and they have certainly done a tidier job than I did with my subset.
  1405. I wouldn't stake my career on this book being right, but I'd probably trust it
  1406. to settle most bar bets.
  1407. Schildt's annotation is the real value added, and it's pretty good. Mostly I
  1408. feel he draws attention to the right sorts of ancillary issues, and he has a
  1409. fairly sensible perspective on the C Standard. You can usually count on him to
  1410. tell you what the words mean and not just what they say.
  1411. His annotation is somewhat compromised by the constraints of the presentation
  1412. -- pages from the C Standard are on the left, his commentary is on the right.
  1413. He's not above continuing a comment onto a subsequent page, but you can still
  1414. see the brakes being applied rather often. Many of his comments and
  1415. illustrations are terse or barely commented to stay roughly in sync with the
  1416. standardese.
  1417. In a few places, I find the annotation excessively terse. Schildt says little
  1418. about the math functions, even though any number of them could profit from
  1419. just a sentence or two. (frexp and ldexp are two that spring to mind.) True,
  1420. many of these functions are ones that only a mathematician could love, but I
  1421. still find rather laconic the assertion, "The descriptions of the hyperbolic
  1422. functions are straightforward and need no further comment." Similarly, the
  1423. functions in <locale.h> get only a cursory treatment, not that I can blame the
  1424. guy for copping out here.
  1425. More generally, Schildt endeavors to say at least something on each topic to
  1426. aid understanding. I don't always agree with what he chose to say, but mostly
  1427. I feel he says something helpful. Only occasionally did I catch him out. For
  1428. example, his description of storage classes, a notoriously involuted topic,
  1429. contains the usual errors of oversimplification. And his recitation of the
  1430. history of register is slightly incorrect and incomplete. But as a rule, I
  1431. think you will be more enlightened than misled by the annotations in this
  1432. book.
  1433. So if you've always wanted your own copy of the C Standard, here's your best
  1434. chance. Not only do you get a reasonably accurate facsimile of the gospel at
  1435. bargain rates, you get some helpful commentary in the bargain. That makes this
  1436. book a better than average buy.
  1437. Title: The Annotated ANSI C Standard
  1438. Author: annotated by Herbert Schildt
  1439. Publisher: Osborne McGraw Hill, 1990
  1440. Price: $39.95
  1441. ISBN: 0-07-881952-0
  1442. Pages: 600
  1443.  
  1444.  
  1445.  
  1446.  
  1447.  
  1448.  
  1449.  
  1450.  
  1451.  
  1452.  
  1453.  
  1454.  
  1455.  
  1456.  
  1457.  
  1458.  
  1459.  
  1460.  
  1461.  
  1462.  
  1463.  
  1464.  
  1465.  
  1466.  
  1467.  
  1468.  
  1469.  
  1470.  
  1471.  
  1472.  
  1473.  
  1474.  
  1475.  
  1476.  
  1477.  
  1478.  
  1479.  
  1480. Porting Microsoft's Foundation Class Library to UNIX
  1481.  
  1482.  
  1483. Scot Wingo and Louis Lu
  1484.  
  1485.  
  1486. Scot is a principal software engineer at Bristol Technology Incorporated,
  1487. where he leads the development of the Wind/U Windows-to-UNIX portability
  1488. toolkit. Louis is also a software engineer at Bristol Technology, where he
  1489. leads the development of Bristol's Xprinter product. Scot and Lu are both on
  1490. the development team that added Microsoft Foundation Class Library support to
  1491. the Wind/U toolkit, allowing MFC 2.0 applications to port to UNIX/Motif. Scot
  1492. and Lu can be reached at (203) 438-6969 or via e-mail at scot@bristol.com and
  1493. lu@bristol.com.
  1494.  
  1495.  
  1496.  
  1497.  
  1498. Introduction
  1499.  
  1500.  
  1501. Many programmers believe that by using C++ with its strong type checking they
  1502. can achieve the multi-platform programmer's nirvana: 100% portable code. We
  1503. tested this theory by porting a large 16-bit based C++ library, Microsoft
  1504. Foundation Class library (MFC), to 32-bit UNIX workstations. We found that
  1505. while using C++ definitely increases your program's portability, it still is
  1506. not the portability silver bullet. This article highlights some common
  1507. portability problems and shows examples of them in the context of MFC.
  1508. Microsoft recently released the second release of MFC with their Visual C++
  1509. development environment. MFC provides the user with the best of both worlds, a
  1510. set of basic data type classes and an application framework. The basic classes
  1511. provide support for collections, exceptions, file I/O, strings, and run-time
  1512. class information. MFC's application framework is built upon the Windows API
  1513. and implements several advanced application features such as:
  1514. toolbars
  1515. status bars
  1516. Multiple Document Interface (MDI)
  1517. Most Recently Used (MRU) -- a list of recently used files in a menu maintained
  1518. for you
  1519. message mapping -- allows you to easily map messages to member functions
  1520. splitter windows -- similar to those used in Excel and Word
  1521. print preview/printing
  1522. Microsoft provides MFC source code with the Visual C++ product as a reference.
  1523. We used this code as the starting point for porting the MFC to UNIX. By making
  1524. MFC available on UNIX, we hope to improve the quality of applications and
  1525. facilitate the creation of feature rich applications on UNIX. Moreover, with
  1526. MFC on both Windows and UNIX, multi-platform developers can support
  1527. applications on both platforms with a single set of source code.
  1528.  
  1529.  
  1530. Getting Started
  1531.  
  1532.  
  1533. After copying the MFC source files to the UNIX workstation, we first had to
  1534. convert the files from DOS format to UNIX format. (DOS files have both
  1535. carriage returns and line feeds. UNIX files have only linefeeds.) Most
  1536. workstations have utilities, such as dos2unix, for performing the conversion.
  1537. Next, we created a quick and dirty makefile that compiles all of the source
  1538. files and stuffs them into a library.
  1539. Before firing up the compiler, we investigated which macros should be defined.
  1540. The first macro, NO_VBX_CONTROLS, excludes the MFC support for Visual Basic
  1541. (VBX) controls. Since there is no concept of Visual Basic controls or the
  1542. underlying library on UNIX, we defined this macro. Microsoft was kind enough
  1543. to also include a PORTABLE macro which, when defined, turns off sections of
  1544. inline Intel x86 assembly code and turns on C++ equivalents. For the initial
  1545. compilation, we also decided to turn on the _DEBUG macro, which turns off most
  1546. inlining and turns on tons of useful assertions. After defining these macros,
  1547. we kicked off the first compile.
  1548. The first portability problems were due to subtle differences in the various
  1549. compilers we used. Most UNIX C++ compilers are based on the AT&T cfront
  1550. implementation. Each hardware vendor typically licenses the cfront compiler
  1551. and adds the platform specific components needed to support their hardware. We
  1552. ported the MFC to Sun SPARCstation and HP 9000/700 workstations. The two
  1553. compilers used in this port were the SPARCworks C++ 3.0.1 and HP's C++ version
  1554. 3.0.2. Both compilers are based on version 3.2 of cfront. Visual C++ uses
  1555. Microsoft's C 8.0 compiler, which claims cfront compliance but is not based
  1556. directly on that implementation.
  1557. The biggest compiler difference was where the _DEBUG version of MFC tracks
  1558. memory allocation by overloading operator new. The debug version of new is
  1559. overloaded to take filename and line number information. Listing 1 shows the
  1560. relevant code.
  1561. When _DEBUG is defined, a new expression should be preprocessed to:
  1562. new ==> DEBUG_NEW
  1563. DEBUG_NEW ==> new(__FILE__, __LINE__)
  1564. CObject *obj = new ( "nested.C" , 30 ) CObject;
  1565. This macro expansion may appear recursive, but it is not. Microsoft C and Sun
  1566. C++ 3.0 expand these macros correctly, but HP C++ 3.0 does not. The HP
  1567. preprocessor fails with the following error message:
  1568. nested.C: 30: Overflowed replacement buffer.
  1569. The HP C++ preprocessor does not follow the macro expansion rules defined in
  1570. The Annotated C++ Reference Manual. In this manual, the C++ ANSI base
  1571. document, section 16.3.3 "Rescanning and Further Replacement" states: "If the
  1572. name of the macro being replaced is found during this scan or during
  1573. subsequent rescanning, it is not replaced." Hopefully, with the publication of
  1574. an ANSI C++ standard, differences like these will no longer be an issue.
  1575. To fix the problem, we just disabled the debug version of new on HP
  1576. workstations by adding:
  1577. #ifndef HPUX
  1578. #define new DEBUG_NEW
  1579. #endif
  1580.  
  1581.  
  1582. Integer Size Issues
  1583.  
  1584.  
  1585. Since integers in the Windows environment are 16 bits wide, C programmers
  1586. often fall into the common mistake of assuming that other 16-bit data types
  1587. are always the same size as an integer. This is not the case in 32-bit
  1588. environments. See Listing 2 for an example of a 16/32-bit problem waiting to
  1589. happen. Porting the code in Listing 2 to UNIX would cause problems if the
  1590. value of nOne was ever greater than 65,535, because it would suddenly become
  1591. too large to fit into wTwo (which is only 16 bits wide). The wTwo variable
  1592. would wrap and start back at 0.
  1593. C++'s strong type checking will never allow code like this to survive, so
  1594. 16/32-bit issues are not usually a common C++ problem. We did find one
  1595. significant 16/32-bit portability problem in the MFC message mapping
  1596. mechanism. To better understand the problem, let's look at how Microsoft has
  1597. implemented Message Mapping in MFC.
  1598. In Windows SDK programming, programs usually handle messages in a window
  1599. procedure, or WinProc. MFC's Message Mapping provides a facility that allows
  1600. you to map a windows message to a C++ class method. This paradigm is a natural
  1601. for object-oriented programming because it lets you think of each message
  1602. handler as being responsible for handling the communication between your
  1603. object and the application framework. Some frameworks use virtual functions
  1604. for message handling, but this results in very large vtables and poor
  1605. performance. Borland's Object Windows Library (OWL) uses a "dynamic dispatch
  1606. table" which is implemented through a new C++ syntax. The drawback of this
  1607. approach is that it requires extensions to the C++ language, and thus is not
  1608. portable. MFC implements message mapping through a set of macros that create a
  1609. message-mapping table inside each class. Here's an example of how to declare a
  1610. simple message map:
  1611. BEGIN_MESSAGE_MAP()
  1612. ON_WM_LBUTTONDOWN()
  1613. ON_WM_LBUTTONUP()
  1614. ON_WM_MOUSEMOVE()
  1615. ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
  1616.  
  1617. ON_COMMAND(ID_FILE_PRINT_PREVIEW,
  1618. CView::OnFilePrintPreview)
  1619. END_MESSAGE_MAP()
  1620. Each entry can use a default mapping such as ON_WM_LBUTTONDOWN, which assumes
  1621. that you would like to map WM_LBUTTONDOWN to the OnLButtonDown member
  1622. function. You can also specify the mapping with the more generic
  1623. ON_MESSAGE(message, function) macro.
  1624. Each entry in the table has the following four elements:
  1625. UINT nMessage
  1626. UINT nID
  1627. UINT nSig
  1628. AFX_PMSG pfn
  1629. where nMessage is the message identifier (such as "WM_PAINT, WM_MOUSEMOVE"),
  1630. nID is the identifier for the recipient of the message, nSig is the signature
  1631. alias (more on this later), and pfn is a pointer to the method for handling
  1632. the specified message.
  1633. The beauty of this message-mapping scheme is that it is very fast (based on an
  1634. integer lookup) and fairly portable. The portability problem comes from the
  1635. way the MFC must store the member function pointers in the table. To avoid
  1636. complete chaos, each table entry uses the nSig field to store the return value
  1637. and argument types of each message handling method. For example, if you have a
  1638. message handler defined as:
  1639. void MessageHandler(WPARAM wParam, LPARAM lParam);
  1640. the nSig value for this function would be AfxSig_vwl. All possible types of
  1641. declarations are enumerated in a MFC header file. This scheme allows the
  1642. message mapping to sneak around C++'s strong type-checking, while still
  1643. providing a level of type checking. When a message comes in, MFC uses the nSig
  1644. value to match the message fields to the fields of the function. The only
  1645. problem with this scheme is that if a function is defined as:
  1646. void MessageHandler(WPARAM wParam,
  1647. CPoint cpoint);
  1648. the nSig value is also AFXSig_vwl. Since the cpoint is treated like a long,
  1649. the CPoint constructor will not be called, and if any conversion other than
  1650. copying needs to happen it will be skipped.
  1651. To fix this problem, we added some new values to the signature enumeration,
  1652. such as AfxSig_vwp, which will ensure that the CPoint constructor is called
  1653. and any conversions are made. The lesson to be learned here is that if you
  1654. circumvent C++'s strong type-checking, you will pay a penalty in portability.
  1655.  
  1656.  
  1657. Alignment and Byte Order
  1658.  
  1659.  
  1660. Another common 16/32-bit problem is structure packing. On 16-bit systems,
  1661. compilers pack structures based on 16-bit boundaries. On 32-bit systems, the
  1662. compilers often use 32-bit boundaries (they waste a byte here and there to
  1663. ensure that the elements of a structure are aligned properly). The end result
  1664. is that the sizeof operator will return different results in 16 and 32-bit
  1665. environments. Structure packing can cause the most problems if you read
  1666. structures to and from binary files. MFC does not write structures to file,
  1667. but does not prevent the programmer from doing so. It is more portable to
  1668. avoid writing structures to file and stick with the basic datatypes when
  1669. writing binary files.
  1670. The other common portability problem between Windows and UNIX is byte
  1671. swapping. Some UNIX workstations such as the Sun SPARCstation, have Big Endian
  1672. (versus Intel's Little Endian) byte ordering. This means, among other things,
  1673. that the programmer cannot make assumptions about the order of the bytes
  1674. within the fields of a structure. C++ does not protect the programmer from
  1675. these problems, and we encountered a significant number of byte-swapping
  1676. problems in MFC. See Listing 3 for a byte-swapping problem in the constructor
  1677. of the MFC class CPoint.
  1678. This code makes the fatal mistake of assuming that data in the DWORD dwPoint
  1679. will be ordered exactly the same as the tagPoint structure. To fix the
  1680. problem, we modified the CPoint constructor to use Microsoft's portable HIWORD
  1681. and LOWORD macros (these live in windows.h) to deconstruct a DWORD properly.
  1682. Here's the portable version of CPoint: :CPoint(DWORD):
  1683. CPoint::CPoint(DWORD dwPoint)
  1684. {
  1685. x = LOWORD(dwPoint);
  1686. y= HIWORD(dwPoint);
  1687. }
  1688. The MFC CPoint and CSize classes contained substantial byte ordering problems
  1689. that we discovered by reviewing the source and scanning for typecasts on the
  1690. left side of expressions.
  1691. Most RISC based systems can only write words to memory on 16-bit boundaries.
  1692. If programs do not follow this rule, a core dump is created with a bus error.
  1693. The MFC object serialization was a source of unaligned write problems, as
  1694. shown in Listing 4. This code assumes that the m_lpBufCur can be written
  1695. without consideration of its alignment in memory. This code caused an
  1696. immediate bus error on both of the target platforms.
  1697. The safest way to avoid these problems is to use the memcpy function, which
  1698. will handle memory alignment for you when necessary. Listing 5 shows the more
  1699. portable version of Listing 4.
  1700.  
  1701.  
  1702. Operating System Differences
  1703.  
  1704.  
  1705. The UNIX systems used here have a flat 32-bit memory scheme, versus DOS's
  1706. segmented memory. MFC has some dependencies on the DOS segmented memory. A
  1707. typical example is:
  1708. #define _AFX_FP_OFF(thing) (*((UINT*)&(thing)))
  1709. #define _AFX_FP_SEG(lp) (*((UINT*)&(lp)+1))
  1710. These macros obtain the segment and offset of a pointer. Needless to say, they
  1711. do not work under UNIX. We replaced each instance of this macro with more
  1712. portable code on a case-by-case basis.
  1713. File system differences are another example of operating-system portability
  1714. problems. The UNIX file system allows file names to be 250 characters long,
  1715. and separates directory names with a / instead of a \ character. DOS file
  1716. names are usually in the format:
  1717. drive_letter:\path\filename.EXT
  1718. where file_name is limited to eight charaacters. The MFC File I/O routines
  1719. contained many problems in this area. So too did the code for serialization
  1720. and MRU.
  1721. MFC uses serialization to provide object persistence in binary files. All
  1722. object serialization is built on the basic types such as WORD, DWORD, float,
  1723. int, etc. Since MFC defines the serialization for these low-level types
  1724. already, developers are isolated from many of the portability problems
  1725. associated with binary file I/O. In the future, we consider re-writing the
  1726. basic type serialization code to be able to read files written on either
  1727. Little or Big Endian machines. To do this, we will always assume that data
  1728. should be written in one byte ordering. If a machine doesn't use that byte
  1729. ordering, the serialization will automatically change to re-order data going
  1730. into and out of binary files via serialization.
  1731.  
  1732.  
  1733. It Works!
  1734.  
  1735.  
  1736. After fixing the mentioned portability problems, we were able to get some MFC
  1737. samples up and running on UNIX, as shown in Figure 1. The porting effort took
  1738. two people approximately two months to examine all of the library and
  1739. eliminate the portability problems. In total there were over 100 portability
  1740. problems that had to be fixed.
  1741. About six months after our port of the 16-bit MFC to UNIX, Microsoft released
  1742. the Windows NT version of MFC. We dissected it to see what portability
  1743. improvements Microsoft had made in their port from the 16-bit Windows
  1744. environment to the 32-bit NT environment. The biggest improvements, as
  1745. expected, were in the areas of 16/32-bit and memory model portability.
  1746. Microsoft fixed all of the examples mentioned earlier in this article, with
  1747. the exception of some byte swapping problems, because NT only runs on Little
  1748. Endian processors. Porting this version of MFC to UNIX will take much less
  1749. time and effort.
  1750. Porting the 16-bit MFC to UNIX was a challenging exercise in finding and
  1751. fixing portability problems. Fellow C++ programmers should take these
  1752. experiences to heart and write code that avoids these portability pitfalls.
  1753. With the multitude of platforms and operating environments available, you
  1754. never know on which platform your code will be running.
  1755.  
  1756.  
  1757. Bibliography
  1758.  
  1759.  
  1760.  
  1761. [1] Microsoft Corp. Microsoft Visual C++ Class Library Reference
  1762. [2] Microsoft Corp. Microsoft Visual C++ Class Library Users' Guide
  1763. [3] Margaret A. Ellis, Bjarne Stroustrup, The Annotated C++ Reference Manual,
  1764. Addison-Wesley, [1990].
  1765. Figure 1 A sample application ported to UNIX
  1766.  
  1767. Listing 1 MFC overloading of new for debugging
  1768. #include <stdlib.h>
  1769. // MFC style debug new defines.
  1770.  
  1771. class CObject
  1772. {
  1773. public:
  1774. #ifdef _DEBUG
  1775. // for file name/line number tracking using DEBUG_NEW
  1776. void* operator new(size_t nSize,
  1777. char * lpszFileName, int nLine);
  1778. #endif
  1779. };
  1780.  
  1781. #ifdef _DEBUG
  1782. // Memory tracking allocation
  1783. #define DEBUG_NEW new(__FILE__, __LINE__)
  1784. #else
  1785. // NonDebug version that assume everything is OK
  1786. #define DEBUG_NEW new
  1787. #endif
  1788.  
  1789. #ifdef _DEBUG
  1790. #define new DEBUG_NEW
  1791. #endif
  1792.  
  1793. main()
  1794. {
  1795. CObject *obj = new CObject;
  1796. }
  1797. // End of File
  1798.  
  1799.  
  1800. Listing 2 A 16/32-bit portability pitfall
  1801. typedef unsigned short WORD;
  1802.  
  1803. int function()
  1804. {
  1805. WORD wOne;
  1806. int nTwo;
  1807. ...
  1808. wTwo = (WORD)nOne;
  1809. ...
  1810. }
  1811. // End of File
  1812.  
  1813.  
  1814. Listing 3 Illustrates byte-swapping problem
  1815. struct tagPOINT
  1816. {
  1817. short x;
  1818. short y;
  1819. };
  1820.  
  1821.  
  1822. class CPoint : tagPOINT {
  1823. ...
  1824. CPoint::CPoint(DWORD);
  1825. ...
  1826. };
  1827.  
  1828. CPoint::CPoint(DWORD dwPoint);
  1829. {
  1830. *(DWORD *)this = dwPoint;
  1831. }
  1832. // End of File
  1833.  
  1834.  
  1835. Listing 4 Code that may create memory alignment problems
  1836. Class CArchive {
  1837. ...
  1838. BYTE * m_lpBufCur;
  1839. BYTE * m_lpBufMax;
  1840. ...
  1841. };
  1842.  
  1843. AFX_INLINE CArchive& CArchive::operator<<(DWORD dw)
  1844. {
  1845. if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax)
  1846. Flush();
  1847. *(DWORD FAR*)m_lpBufCur = dw;
  1848. m_lpBufCur += sizeof(DWORD);
  1849. return *this;
  1850. }
  1851.  
  1852. // End of File
  1853.  
  1854.  
  1855. Listing 5 Fixes potential memory alignment problems in Listing 4
  1856. AFX_INLINE CArchive& CArchive::operator<<(DWORD dw)
  1857. {
  1858. if (m_lpBufCur + sizeof(DWORD) > m_lpBufMax)
  1859. Flush();
  1860.  
  1861. memcpy(m_lpBufCur,&dw,sizeof(DWORD));
  1862.  
  1863. m_lpBufCur += sizeof(DWORD);
  1864. return *this;
  1865. }
  1866.  
  1867. // End of File
  1868.  
  1869.  
  1870.  
  1871.  
  1872.  
  1873.  
  1874.  
  1875.  
  1876.  
  1877.  
  1878.  
  1879.  
  1880.  
  1881.  
  1882.  
  1883.  
  1884.  
  1885. Handling Time-Consuming Windows Tasks
  1886.  
  1887.  
  1888. Andy Yuen
  1889.  
  1890.  
  1891. Andy Yuen has a Master and a Bachelor degree in Electrical Engineering from
  1892. Carleton University in Ottawa, Canada. He has been working in the software
  1893. industry for over 14 years. He is currently a senior software engineer with a
  1894. company in Sydney, Australia, which specializes in automation and network
  1895. management. Andy can be reached by e-mail at auawyl@grieg.sdi.oz.au.
  1896.  
  1897.  
  1898.  
  1899.  
  1900. Introduction
  1901.  
  1902.  
  1903. Microsoft Windows 3.X and OS/2 Presentation Manager (PM) programmers are
  1904. probably all aware of the "1/10 second rule" -- an application should take no
  1905. longer than 1/10 of a second to process a message. Any task which takes longer
  1906. can be considered a "big job." And a big job degrades the system response.
  1907. Sometimes, it even brings the system to a standstill while it is running. This
  1908. happens because there is only one input message queue for all Windows or PM
  1909. applications. Once you hold up this queue, other applications cannot run.
  1910. OS/2, to some extent, is less affected by a big job than Windows due to its
  1911. true multitasking capability; other non-PM tasks can still run. Also, OS/2 has
  1912. a lot more built-in facilities to combat this problem. (For example, see the
  1913. sidebar on OS/2's object windows.)
  1914. The usual work-around under Windows is to have PeekMessage loops scattered all
  1915. through a program. This article proposes an alternate solution. It describes
  1916. the small C++ class Cschlr which provides the functions of a simple scheduler.
  1917. It manages thread creation, deletion, and synchronization by using counting
  1918. semaphores and timed functions. A Windows program can create one or more
  1919. threads to handle time-consuming tasks and then pass control back to Windows
  1920. quickly via the message loop. Thread interactions can be synchronized by using
  1921. counting semaphores and timed functions. Although this C++ class has been
  1922. designed from the ground up for Windows, it can actually be used unmodified
  1923. for OS/2. I also discuss a number of the design/implementation issues, and
  1924. illustrate its use with a complete example.
  1925.  
  1926.  
  1927. Design
  1928.  
  1929.  
  1930. The design criteria of Cschlr are based on the KISS (Keep-It-Simple-Stupid)
  1931. principle. Consequently, Cschlr must be simple, small, and portable. It is
  1932. intended to work in the following fashion:
  1933. 1. Threads can be created before entering the main PeekMessage loop or can be
  1934. created anywhere in an application.
  1935. 2. Threads can terminate themselves anywhere within an application.
  1936. 3. Parameters can be passed to threads during thread creation.
  1937. 4. PeekMessage needs only be used once in WinMain.
  1938. 5. Counting semaphores and timed functions provide synchronization among
  1939. threads.
  1940. The interface to the Cschlr class (C++ header file) can be found in Listing 1.
  1941. Member functions can be found in Listing 2. The constructor and destructor are
  1942. self-explanatory. I give below a brief description of the rest of the methods:
  1943. int CreateThread(THDFN func, int stacksize, void *param);
  1944. Used to create a thread, where func is the thread procedure, stacksize is the
  1945. size of the stack for the thread and param is a pointer to user parameters to
  1946. be passed to the thread. This method is modelled after the BeginThread
  1947. function provided by C compilers for OS/2. The returned integer is the thread
  1948. number.
  1949. void Suicide();
  1950. The counterpart of CreateThread. A thread can kill itself by either calling
  1951. Suicide explicitly or by letting itself to fall through the end of the thread
  1952. function.
  1953. Csemq* CreateSem(long lValue);
  1954. Used to create a counting semaphore and to initialise its value to lValue. It
  1955. returns the semaphore object on success or a null pointer on failure.
  1956. void Signal(Csemq *sem, long lMaxCount = 256);
  1957. Used to signal a semaphore. Sem is a semaphore object created by CreateSem.
  1958. lMaxCount is the maximum semaphore count allowed. If the current semaphore
  1959. count is greater than or equal to lMaxCount, its count will not be increased
  1960. any further. The calling thread always gets back control after calling Signa1.
  1961. (Refer to the sidebar for a description of the operation of counting
  1962. semaphores)
  1963. void Wait(Csemq *sem);
  1964. Used to wait on a semaphore. If the semaphore count is smaller than 1, the
  1965. calling thread will be blocked until signalled by another thread.
  1966. void Preempt();
  1967. Used by the calling thread to give up the CPU voluntarily.
  1968. void Sleep(long 1Seconds);
  1969. Used by the calling thread to go to sleep for lSeconds. Another ready-to-run
  1970. thread will be despatched.
  1971. void GetSemStates(Csemq *sem, long &lCount, int &fWait);
  1972. Used to retrieve the current states of the semaphore object sem. On return,
  1973. lCount contains the semaphore count and fWait is set to TRUE if there is at
  1974. least one thread waiting on the semaphore and FALSE otherwise.
  1975. void Run();
  1976. Used to dispatch threads and to give them a slice of the CPU. This method is
  1977. normally only invoked in the main PeekMessage loop.
  1978.  
  1979.  
  1980. Implementation
  1981.  
  1982.  
  1983. Cschlr implements the high-level thread and semaphore management services and
  1984. provides the interface to the user. It maintains a ready-to-run queue for
  1985. threads and keeps tracks of the identity of the running thread. The state
  1986. transition diagram of Cschlr is shown in Figure 1. Preempt handles the task
  1987. switch. It moves the running thread to the ready-to-run queue and schedules
  1988. another thread from the ready-to-run queue for execution. Wait allows the
  1989. running thread to wait in a semaphore queue while another ready-to-run thread
  1990. is dispatched. Signal moves a thread waiting in a semaphore queue back to the
  1991. ready-to-run queue. And Sleep and Wakeup provide time-delay services to all
  1992. threads. Please note that Wakeup is used only internally by Cschlr.
  1993. In order to implement the above, Cschlr calls on the services of two private
  1994. classes: Csemq (see Listing 3 and Listing 4) and Cthread (Listing 5 and
  1995. Listing 6). Consequently, it is easy to understand how Cschlr works once one
  1996. knows the internals of Csemq and Cthread. Hence, I'll start with these and
  1997. come back to Cschlr later.
  1998. Csemq is a private class of Cschlr. This means that all of its methods and
  1999. data are private to the containing class. Only Cschlr can access them, besides
  2000. Csemq itself, because Cschlr has been declared as a friend of Csemq.
  2001. Csemq provides semaphore services to Cschlr. There is a counter and a queue
  2002. associated with each counting semaphore. Most C++ compilers come with class
  2003. libraries which provide common objects like lists, stacks, queues, etc.
  2004. However, if I had used any one of these class libraries, the code would no
  2005. longer be portable among different compilers. Also, in examining the
  2006. requirements, and sticking to the small-is-beautiful design philosophy, I
  2007. decided to implement my own queue management routines because I don't need the
  2008. full functions of these class libraries anyway.
  2009. I use a long integer to represent both a queue and a counter. If the MSB (Most
  2010. Significant Bit) is a 0, the long integer is a counter, which means that the
  2011. counter is at most 31 bits long. When the MSB is 1, the long integer is a
  2012. queue. Each set bit (non-zero bit) in the long integer represents a thread.
  2013. For example, a value of 0x8003 contains two threads: bit 0 is thread #0, and
  2014. bit 1 is thread #1. This implies that there are at most 31 threads possible.
  2015. This should be more than enough for our purpose.
  2016. If multiple threads are waiting on a semaphore, which thread should we resume
  2017. when a signal has been received? I decided that a somewhat round-robin
  2018. algorithm should be implemented. I said "somewhat" because it is not possible
  2019. to implement a truly round-robin algorithm using the queue implementation
  2020. described above -- such a queue does not keep the order in which threads are
  2021. queued. (Someone might point out that this is a contradiction in terms, and
  2022. that it is not really a queue if it does not maintain the order. Maybe I
  2023. should have called it an unordered list. Anyway, I'll refer to it as a queue
  2024. throughout this article.)
  2025.  
  2026. In order to simulate a round-robin effect, each Csemq object has a private
  2027. variable priority associated with it. Priority is set to CSC_NO_THREAD (whose
  2028. value is 31) when a Csemq object is created. Method Dequeue returns a thread
  2029. number for Cschlr to schedule its execution. It starts scanning the semaphore
  2030. queue one bit to the left of the bit position recorded in priority. Since
  2031. priority is set to 31, it starts scanning from bit 0 (wraparound) moving to
  2032. bit 1, bit 2, etc.
  2033. When a thread is found, its position is saved in priority. For example, if
  2034. thread 4 (bit 4) is the first thread found waiting on the semaphore, Dequeue
  2035. starts scanning from bit 5 the next time it is invoked. The reason that I go
  2036. to such trouble to implement this scheduling algorithm is to prevent a thread
  2037. from monopolizing the CPU. Take for example the following case: thread 0 and
  2038. thread 1 are waiting on the same semaphore. Assume that thread 0 starts
  2039. execution and later waits on the same semaphore. If I start scanning always
  2040. from bit 0, thread 0 will always get awakened when the semaphore gets
  2041. signalled. Thread 1 will never get a chance to run. By implementing the
  2042. aforementioned "somewhat" round-robin scheduling algorithm, this situation
  2043. could be avoided.
  2044. Semaphore operations such as Signal and Wait should be atomic or indivisible.
  2045. They cannot be interrupted in the middle of an operation. If the operation is
  2046. not atomic, one might get into the situation where two threads try to perform
  2047. an operation on the same semaphore concurrently. It may happen that while one
  2048. operation is half-way through the operation, the operating system switches to
  2049. another thread which then updates the semaphore count or queue, rendering the
  2050. semaphore structure inconsistent. Under Windows, this would never happen
  2051. because of the cooperative multitasking model used: a Windows procedure only
  2052. gives up the CPU voluntarily. There is no chance of having two truly
  2053. concurrent threads of execution under Windows. The PeekMessage loop is the
  2054. mechanism to multiplex the CPU among a number of pseudo-concurrent threads in
  2055. an application which uses Cschlr.
  2056.  
  2057.  
  2058. Class Cthread
  2059.  
  2060.  
  2061. Cthread is also a private class of Cschlr. It provides thread objects to
  2062. Cschlr. In order to keep this class implementation portable, I avoided the use
  2063. of inline assembly since the syntax is different among compilers. The most
  2064. portable way to implement this is to use the Standard C library's setjmp and
  2065. longjmp functions. These were originally designed for error recovery purposes
  2066. and people are starting to question their usefulness under C++, which has
  2067. exception handling. However, these two funcitons prove to be invaluable in
  2068. implementing the Cthread class.
  2069. Unfortunately, the use of setjmp and longjmp do not make Cthread 100 per cent
  2070. portable, due to the layout of the jmp_buf structure. jmp_buf is used to save
  2071. the context of execution when setjmp is called. The execution context consists
  2072. of various CPU registers. The only time that I need to know which registers go
  2073. where is during the creation of a thread. One needs to set the CS:IP (Code
  2074. Segment:Instruction Pointer) and the SS:SP (Stack Segment:Stack Pointer) to
  2075. simulate a setjmp during thread creation.
  2076. Cthread maps the jmp_buf to a data structure defined within CTHREAD. CPP. The
  2077. typedef statement:
  2078. typedef struct {
  2079. long ip; //CS:IP
  2080. short filler; //don't care
  2081. long sp; //SS:SP
  2082. } *mapptr;
  2083. defines a pointer type for this purpose. It works for the Zortech C++ Version
  2084. 3 compilers for Windows and OS/2, which I am using for the development of
  2085. Cschlr. If you are using a different compiler, you need to change this type
  2086. definition to reflect the difference in implementation of these functions.
  2087. There are two ways to find out the structure of your compiler's jmp_buf. The
  2088. first is to examine the sources for the C library if they are available. The
  2089. second method, and probably the easier of the two, is to write a very simple C
  2090. program which calls setjmp. Compile and link it with debugging information.
  2091. Then use the source-code debugger to compare the register and jmp_buf values
  2092. before and after executing setjmp to find out where the CS:IP and SS:SP
  2093. registers get saved.
  2094. The other thing to remember is that Cthread is intended to be compiled using
  2095. the large memory model. The magic number WORDSTOREMOVE is defined as 3.
  2096. Different values are needed for different memory models. The magic number is 3
  2097. because the C library function setjmp has the form:
  2098. int setjmp(jmp_buf env);
  2099. A thread pushes env onto the stack, and then calls setjmp, thus saving the
  2100. 32-bit return address on the stack. setjmp then saves the BP (Base Pointer)
  2101. register on the stack (the standard C function prologue). When longjmp is
  2102. called to restore the stack environment, it discards the saved BP and return
  2103. address on the stack. For the large memory model, the return address occupies
  2104. four bytes while BP occupies two bytes, for a total of three 16-bit words.
  2105. Since this function relies on the standard C prologue to work properly, the
  2106. compiler must be instructed to generate the standard C function prologue
  2107. instead of the one for Windows. Consequently, you will notice that there are
  2108. two different compile option macros defined in the Makefile (Listing 8) for
  2109. the example program: one for compiling Cschlr, Csemq, and Cthread, the other
  2110. for compiling the Windows main program.
  2111. Cthread saves the context of a thread in the data structure THD. The member
  2112. Context is the jmp_buf. TotalLen is the total length of the THD structure in
  2113. bytes. This value is needed for the destructor of a thread object, which frees
  2114. the allocated storage for the stack. OverFlowed is a flag set to zero to guard
  2115. against stack overflow. Stack is the stack for the thread. Since the stack
  2116. grows downward in a X86 processor, if overflow occurs Overflowed will
  2117. hopefully be overwritten with a nonzero value. Cthread refuses to switch to a
  2118. thread with a nonzero OverFlowed flag when the Transfer method is invoked.
  2119. The implementation of Cschlr is quite straightforward except for CreateThread,
  2120. which passes a user parameter to the thread, and the implicit termination of a
  2121. thread by falling through the thread function. Again, Cschlr is designed for
  2122. the large memory model. CreateThread creates a new thread by:
  2123. task[i] = new Cthread(func, stacksize, (int *) &retaddr, 4);
  2124. The argument value 4 specifies the number of 16-bit words to copy to the new
  2125. thread's stack. In order to force a thread to kill itself when falling through
  2126. the thread function body, a return address is copied to the stack, together
  2127. with the user parameter to be passed to the thread. This return address points
  2128. to Kill, a static function within CTHREAD.CPP. See Figure 2 for the stack
  2129. layout during thread creation. When the thread exits the thread function, it
  2130. returns to Kill, which calls Suicide to terminate itself. The address of Kill
  2131. and the user parameter pointer take up four words when the large memory model
  2132. is used.
  2133. Note also that there are 33 slots reserved:
  2134. Cthread *task[CSC_NO_THREAD + 3];
  2135. in the Cschlr object although there can only be 31 threads. One extra slot is
  2136. used for the Windows main program while the other is for termination of
  2137. threads. When Run, the method that multiplexes the CPU among various threads,
  2138. is called within the PeekMessage loop, its context gets saved in the slot
  2139. task[MAIN]. When a thread gives up the CPU voluntarily, control is passed back
  2140. to MAIN so that it can continue with the PeekMessage loop. Consequently, a
  2141. Wait or a Sleep must not be called within the PeekMessage loop. Otherwise it
  2142. will bring back the big-job problem that we meant to avoid.
  2143. When Sleep is called, the thread is put to sleep in the dummy semaphore queue
  2144. WaitQ. All sleeping threads are maintained in chronological order in the form
  2145. of a linked list. During each task switch, the table is scanned and the
  2146. threads that have reached the end of their sleep are moved to the ready-to-run
  2147. queue ReadyQ. If there is no ready-to-run thread in ReadyQ, control is
  2148. returned to MAIN immediately.
  2149. The slot task[DUMMY] is used for thread termination. It is required because a
  2150. Cthread object always saves the context of the running thread during a task
  2151. switch. task[DUMMY] is used for saving the context of the terminating thread
  2152. temporarily. The context saved in it never needs to be restored because that
  2153. thread no longer exists after committing suicide.
  2154.  
  2155.  
  2156. An Example
  2157.  
  2158.  
  2159. The best way to understand Cschlr is by example. Charles Petzold describes the
  2160. Salvage benchmark as an example of a "big job" [2]. I'll use the same example
  2161. to contrast the Cschlr solution to the big job to the solutions described in
  2162. his article. Instead of starting from scratch, I'll use Gpf Systems Inc.'s
  2163. OS/2-based Gpf code generator. My version of Gpf is Version 1.3, which runs on
  2164. OS/2 V1.3. It is a tool very similar to Microsoft's Visual C++. However, it
  2165. generates Windows, OS/2 16-bit, and 32-bit C code.
  2166. It was ahead of its time when I first got it in early 1992. I believe the
  2167. latest version is Gpf 2.0. Instead of going into details as to how it works,
  2168. let me just say that it generates the framework for a Windows applications and
  2169. creates the necessary resources. I am just using it as an event dispatcher,
  2170. which means that whenever an event occurs -- the user clicks on a menu item,
  2171. for example -- Gpf will call my function for handling that event.
  2172. The example program is called BIGJOB.CPP. [Note: BIGJOB.CCP is not listed here
  2173. because of its size. It will be on the monthly code disk. See BIGJOB.H,
  2174. Listing 7, which implements the main logic of the example. -- mb] BIGJOB has
  2175. only two menu items: Repetition and Action. Repetition allows a user to select
  2176. the number of iterations to run the Salvage benchmark and Action allows a user
  2177. to either Start or Abort its execution. Running it for 10,000 iterations may
  2178. take quite a while depending on the speed of your system. BIGJOB displays a
  2179. progress report every five seconds and shows the total time taken for running
  2180. the benchmark to completion. While the benchmark is running, a user can switch
  2181. to any Windows application at will without appreciable delay. An application
  2182. which is not designed to execute a big job will lock up the computer for the
  2183. duration of the benchmark. This example clearly demonstrates the power of
  2184. Cschlr.
  2185. As I have said before, I am using Gpf as an event dispatcher. Whenever an
  2186. event occurs, one of my functions gets called. The only thing I need to change
  2187. in the Gpf generated source code is to replace the generated main message loop
  2188. with my PeekMessage loop. In order to expedite the development, the body of my
  2189. event handling functions (the file BIGJOB.H) is included in BIGJOB.CCP by an
  2190. #include directive. People may frown at this method, but it is actually
  2191. suggested in the Gpf manual [4].
  2192. Another thing that needs to be done is to rename the generated file BIGJOB.C
  2193. to BIGJOB.CPP in order to use C++ classes. Some may find Gpf's generated
  2194. comments to be excessive. Other may find that, for such a small application,
  2195. the generated code is more complex than necessary and many functions are
  2196. included although they never get used. This is no longer true if you use Gpf
  2197. to develop a large application. After all, there is always a price to pay for
  2198. the convenience one gets by using Gpf.
  2199. The events that I hook into include:
  2200.  
  2201.  
  2202. WM_CREATE
  2203.  
  2204.  
  2205. Gpf calls CreateThread(VOID) to create two threads: Timer and Big. Timer is
  2206. responsible for the periodic progress report when the benchmark is running. It
  2207. invalidates the main window to force PaintWindow(pGpfParms) to repaint the
  2208. screen. Note that Gpf usually passes a pointer of type PGPFPARMS, which
  2209. contains all windows-related parameters like handles, messages, etc. to a
  2210. user-supplied function for event handling.
  2211. Big is the thread that actually carries out the benchmark. Notice that Big
  2212. gives up the CPU voluntarily after each invocation of Salvage by calling
  2213. mtask.Preempt(). This allows the main PeekMessage loop to regain control so
  2214. that other Windows applications may run. It also examines fContinue to see
  2215. whether it should abort the benchmark and enable the Start item in the
  2216. application menu.
  2217.  
  2218.  
  2219. WM_PAINT
  2220.  
  2221.  
  2222. Gpf calls PaintWindows(pGpfParms) which displays the appropriate message on
  2223. the screen.
  2224.  
  2225.  
  2226. WM_COMMAND
  2227.  
  2228.  
  2229.  
  2230. Gpf calls SelectRep(pGpfParms) which sets the number of repetitions and puts a
  2231. check mark in the selected item by calling the Gpf function GpfMenuTick.
  2232. Gpf calls StartSalvage(pGpfParms) to disable the item Start to avoid further
  2233. selection. The function then enables the Abort item, sets the boolean variable
  2234. fContinue to TRUE, and signals the Big thread to carry out the benchmark.
  2235. AbortSalvage(pGpfParms) sets fContinue to FALSE, disables the Abort item, and
  2236. enables the Start item by calling the Gpf function GpfMenuGray.
  2237. Semaphores semtimer and semjob are used to synchronize the actions among the
  2238. Window procedure and the threads Timer and Big.
  2239. PeekMessageLoop is the function that replaces the normal GetMessage loop in
  2240. WinMain.
  2241.  
  2242.  
  2243. Cross-platform Development
  2244.  
  2245.  
  2246. Cschlr works under both Windows and 16-bit OS/2 PM. Since I am using compilers
  2247. from the same vendor, I don't even have to make any change to the mapptr type
  2248. definition in Cthread because the jmp_buf structure remains unchanged for both
  2249. Windows and OS/2 compilers. Cschlr works under OS/2 because OS/2's PM also has
  2250. the same architecture as Windows: it uses a single message queue for all PM
  2251. applications.
  2252. Cschlr does not take advantage of the true multi-tasking (multi-threading)
  2253. capabilities of OS/2. It dispatches threads by multiplexing the CPU with Run()
  2254. in the main PeekMessage loop in Windows and the WinPeekMsg loop in OS/2. But
  2255. it has one advantage that true OS/2 multi-tasking does not have. OS/2 threads
  2256. cannot directly force an event to occur by calling APIs like WinInvalidateRect
  2257. if the calling thread is not the message thread (the thread where the message
  2258. queue is created). Cschlr allows you to do that because all threads it creates
  2259. are actually part of the message thread.
  2260. Although one can use Gpf to define the screen layout and use the same Gpf file
  2261. to generate both Windows and OS/2 C source codes, the user-supplied event
  2262. handling functions still need to be changed if they use GUI-specific calls,
  2263. due to the differences between Windows and PM APIs. This brings one to ask the
  2264. question: is there a tool which allows you to develop an application once and
  2265. port it to other platforms without any source changes? The answer is both yes
  2266. and no. Yes, there are some tools in the market which claim to do that. No,
  2267. they may not do what you intend them to do.
  2268. Most of these tools take the common denominator approach, which limits you to
  2269. use only a small subset of features available from a particular GUI. Although
  2270. they usually provide ways for you to handle GUI-specific features, once you've
  2271. done that, the application is no longer portable. Some class libraries attempt
  2272. to encapsulate GUIs in C++ and force you to learn a totally new set of
  2273. functions and programming model to gain portability. Some of them have over
  2274. 150 classes with thousands of methods. The learning curve is steep even if
  2275. they can do what they claim to do.
  2276. My friends and I did some extensive research on cross-platform tools sometime
  2277. ago. The verdict: package the non-GUI-related functions in the most portable
  2278. way possible and use the best tool you can find to generate the particular GUI
  2279. that you are interested in. If the non-GUI part -- the heart of your
  2280. application -- is well packaged, it should be possible to interface the two
  2281. components together. Of course, it is easier said than done. The best way to
  2282. do this varies depending on the application. Since Cschlr works unmodified
  2283. under both Windows and OS/2, I think it should qualify as a cross-platform
  2284. tool.
  2285.  
  2286.  
  2287. Conclusions
  2288.  
  2289.  
  2290. I have presented a simple C++ class, Cschlr, to handle time-consuming tasks
  2291. under Windows, and demonstrated its use by a complete example. It should be
  2292. evident from the example that Cschlr helps in limiting the proliferation of
  2293. PeekMessage loops that are quite commonplace in applications that are CPU
  2294. bound. It reduces the total number of PeekMessage loops to exactly one and
  2295. provides counting semaphores and timed functions for synchronisation.
  2296. All these functions are provided without increasing the memory usage
  2297. significantly, due to the small size of Cschlr. The use of Cschlr also
  2298. improves the readability of source code by reducing the proliferation of
  2299. PeekMessage loops and by providing a facility for the user to organize a
  2300. Windows application by breaking it up into smaller and more manageable pieces
  2301. in the form of threads. And a Windows application written using threads should
  2302. make porting to OS/2 less painful.
  2303. References
  2304. 1. Dror, A., Lafore, R. 0S/2 Presentation Manager Programming Primer.
  2305. McGraw-Hill 1990.
  2306. 2. Petzold, C. Utilizing OS/2 Multithread Techniques in Presentation Manager
  2307. Applications. Microsoft Journal, March 1988.
  2308. 3. Comer, D., Fossum, T. V. Operating System Design Vol. 1. The Xinu Approach.
  2309. Prentice-Hall 1988.
  2310. 4. Gpf Systems, Inc. Gpf. Gpf Systems Inc. 1992.
  2311. Counting Semaphores
  2312. Semaphores are special constructs invented by E. W. Dijkstra for concurrent
  2313. process synchronization. Only two operations are allowed on semaphores: wait
  2314. and signal (originally called P and V operations respectively by Dijkstra).
  2315. There is usually a count and a queue associated with each semaphore. The wait
  2316. and signal operations must be done indivisibly, in the sense that only one
  2317. process can operate on a semaphore at any one time. This can be achieved on a
  2318. single-processor system like the PC by simply disabling interrupts during such
  2319. operations. Wait and signal may be implemented as follows:
  2320. wait(S): If the count of semaphore S is greater than zero, subtract 1 from it
  2321. and continue, otherwise put the calling thread in semaphore S's queue and
  2322. schedule the execution of another ready-to-run thread.
  2323. signal(S): If no thread is waiting on semaphore S, add 1 to its count and
  2324. continue, otherwise move the first thread waiting in semaphore S's queue to
  2325. the ready-to-run queue.
  2326. The wait operation allows a process or thread to relinquish use of the CPU to
  2327. other ready-to-run processes or threads when it is waiting on a certain event
  2328. to occur. When such an event occurs, a signal operation can be used to inform
  2329. the waiting process to proceed with its normal operation.
  2330. A semaphore can be thought of as a jar holding cookies. The count associated
  2331. with the semaphore corresponds to the number of cookies in the jar. Each wait
  2332. operation is analogous to removing a cookie from the jar. When the jar is
  2333. empty, one has to wait until someone tosses in some more cookies (signal
  2334. operations).
  2335. Semaphores serve many useful purposes. For example, they may be used to guard
  2336. against concurrent access of shared variables among a number of concurrent
  2337. threads, or to synchronize the execution of two threads. The following example
  2338. demonstrates the latter:
  2339. Thread #1 Thread #2
  2340.  
  2341. {processing) {processing}
  2342.  
  2343. wait(checkpoint) signal(checkpoint)
  2344.  
  2345. {processing} {processing}
  2346. Here, checkpoint is a semaphore with a count of zero. The jar is empty. When
  2347. thread #1 performs the wait operation, it blocks itself (since the jar is
  2348. empty) until thread #2 signals it to resume (puts a cookie in the jar). And
  2349. this concludes our short introduction to counting semaphores.
  2350. Object Windows
  2351. All windows under OS/2's Presentation Manager are objects. The windows that
  2352. people are familiar with all manifest themselves on the screen -- scroll bars,
  2353. child windows, etc. Object windows, on the other hand, do not have this visual
  2354. property. Since they do not display anything on the screen, they don't have to
  2355. handle many of the events like WM_PAINT, WM_CHAR, etc. They are free to do
  2356. whatever a programmer intends them to do, including carrying out a lengthy
  2357. task and receiving messages at the same time. A programmer defines all
  2358. messages going to an object window and all messages that an object window
  2359. sends. An object window communicates with other windows by posting of these
  2360. messages.
  2361. Figure 1 State transition diagram of the Cschlr scheduler
  2362. Figure 2 Stack layout during thread creation
  2363.  
  2364. Listing 1 Interface to Cschlr class
  2365. #ifndef__CSCHLR
  2366. #define__CSHLR
  2367.  
  2368. #include <time.h>
  2369. #include "cthread.hpp"
  2370. #include "csemq.hpp"
  2371.  
  2372. struct schlr_table
  2373. {
  2374. time_t wakeuptime;
  2375. int next;
  2376.  
  2377. };
  2378.  
  2379. class Cschlr
  2380. {
  2381. private:
  2382. Csemq readyQ; //ready-to-run queue
  2383. Csemq waitQ; //queue for delayed
  2384. //threads to wait on
  2385. Cthread *task[CSC_NO_THREAD + 3]; //thread objects
  2386. schlr_table *table; //delay table
  2387. //(linked list)
  2388. int head; //head of linked list
  2389. int nTask; //number of threads
  2390. //created
  2391. int nRunning; //running thread number
  2392.  
  2393. void Switch(Csemq *sem);
  2394. void WakeUp();
  2395.  
  2396. public:
  2397. Cschlr();
  2398. ~Cschlr();
  2399. int CreateThread(THDFN func, int stacksize, void *param);
  2400. void Suicide();
  2401. Csemq* CreateSem(long 1Value);
  2402. void DestroySem(Csemq *sem);
  2403. void Signal(Csemq *sem, long 1MaxCount = 256);
  2404. void Wait(Csemq *sem);
  2405. void Preempt();
  2406. void Sleep(long 1Seconds);
  2407. void GetSemStates(Csemq *sem, long &lCount, int &fWait);
  2408. void Run();
  2409.  
  2410. };
  2411.  
  2412. #endif
  2413.  
  2414. // End of File
  2415.  
  2416.  
  2417. Listing 2 Member function definitions for class Cschlr
  2418. #include <stdlib.h>
  2419. #include <stdio.h>
  2420. #include "cschlr.hpp"
  2421.  
  2422. const int MAIN = CSC_NO_THREAD; //main prgram context
  2423. const int DUMMY = MAIN + 1; //temporary context
  2424. const int EMPTY =-1; //no delayed thread marker
  2425.  
  2426. /* The following static varaible is used to facilitate the
  2427. implementation of Kill. This limits the number of Cschlr
  2428. object per Windows application to 1 only */
  2429. static Cschlr *schlr;
  2430.  
  2431. /* Used for the implicit termination of a thread when
  2432. execution falls through the thread function body */
  2433. static void Kill()
  2434. {
  2435. schlr->Suicide();
  2436.  
  2437. }
  2438.  
  2439. /* Used to check if it is time to wake up the sleeping threads. */
  2440. void Cschlr::WakeUp()
  2441. {
  2442. time_t current;
  2443. schlr_table *ptr;
  2444.  
  2445. /* check time and dispatch thread */
  2446. time(¤t);
  2447. while ((head != EMPTY) &&
  2448. (current >= (ptr = (table + head))->wakeuptime))
  2449. {
  2450. readyQ.Enqueue(head);
  2451. head = ptr->next;
  2452. ptr->next = EMPTY;
  2453. }
  2454. }
  2455.  
  2456. /* Used to perform a task switch. Control is passed
  2457. back to the Windows PeekMessage loop */
  2458. void Cschlr::Switch(Csemq *sem)
  2459. {
  2460. WakeUp();
  2461. sem->Enqueue(nRunning);
  2462. task[nRunning]->Transfer(*task[MAIN]);
  2463. }
  2464.  
  2465. /* Constructor to set up the task table */
  2466. Cschlr::Cschlr()
  2467. {
  2468. nTask = 0;
  2469. nRunning = CSC_NO_THREAD;
  2470. head = EMPTY;
  2471. table = new schlr_table[CSC_NO_THREAD];
  2472. schlr = this;
  2473.  
  2474. //used to resume main program
  2475. task[MAIN] = new Cthread(NULL, 0, NULL, 0);
  2476.  
  2477. //used in Suicide to switch task
  2478. task[DUMMY] = new Cthread(NULL, 0, NULL, 0);
  2479. }
  2480.  
  2481. /* Empty destructor */
  2482. Cschlr::~Cschlr()
  2483. {
  2484. //do nothing
  2485. }
  2486.  
  2487. /* Used to create a thread and pass to it a user variable */
  2488. int Cschlr::CreateThread(THDFN func, int stacksize, void *param)
  2489. {
  2490. THDFN retaddr; //these two variables must appear
  2491. void *ptr; //together for CreateThread to work
  2492. int i;
  2493.  
  2494. int thread = CSC_NO_THREAD;
  2495.  
  2496.  
  2497. if (nTask < CSC_NO_THREAD + 1)
  2498. {
  2499. for (i = 0; i < CSC_NO_THREAD + 1; i++)
  2500. {
  2501. if (task[i] == NULL)
  2502. break;
  2503. }
  2504. retaddr = Kill; //set return address to point to Kill
  2505. ptr = param; //set user parameter pointer
  2506. task[i] = new Cthread(func, stacksize, (int *) &retaddr, 4);
  2507. readyQ.Enqueue(i);
  2508. thread = nTask++;
  2509. }
  2510.  
  2511. return (thread);
  2512. }
  2513.  
  2514. /* Used by a calling thread to commit suicide or self-terminate */
  2515. void Cschlr::Suicide()
  2516. {
  2517. int current;
  2518.  
  2519. current = nRunning;
  2520. nRunning = readyQ.Dequeue();
  2521. delete task[current];
  2522. task[current] = NULL;
  2523. nTask--;
  2524. task[DUMMY]->Transfer(*task[nRunning]);
  2525. }
  2526.  
  2527. /* Used to create a semaphore object */
  2528. Csemq* Cschlr::CreateSem(long lValue)
  2529. {
  2530. return (new Csemq(lValue));
  2531. }
  2532.  
  2533. /* Used to destroy a semaphore object */
  2534. void Cschlr::DestroySem(Csemq* sem)
  2535. {
  2536. delete sem;
  2537. }
  2538.  
  2539. /* Used to signal a semaphore */
  2540. void Cschlr::Signal(Csemq *sem, long 1MaxCount)
  2541. {
  2542. if (sem->GetType() == CST_COUNT)
  2543. {
  2544. if (sem->GetCount() < lMaxCount)
  2545. sem->UpdateCount(1);
  2546. }
  2547. else
  2548. {
  2549. readyQ.Enqueue(sem->Dequeue());
  2550. }
  2551. }
  2552.  
  2553. /* Used to wait on a semaphore */
  2554. void Cschlr::Wait(Csemq *sem)
  2555. {
  2556.  
  2557.  
  2558. if (sem->GetType() == CST_COUNT)
  2559. {
  2560. if (!sem->GetCount())
  2561. {
  2562. //move running thread to semaphore queue and
  2563. //switch to a ready-to-run thread
  2564. Switch(sem);
  2565. }
  2566. else
  2567. //decrement sempahore count and continue execution
  2568. sem->UpdateCount(-1);
  2569. }
  2570. else
  2571. {
  2572. //move running thread to semaphore queue and
  2573. //switch to a ready-to-run thread
  2574. Switch(sem);
  2575. }
  2576. }
  2577.  
  2578. /* Used to give up the cpu voluntarily */
  2579. void Cschlr::Preempt()
  2580. {
  2581. Switch(&readyQ);
  2582. }
  2583. /* Used to put the calling thread to sleep for the
  2584. specified number of seconds */
  2585. void Cschlr::Sleep(long lseconds)
  2586. {
  2587. time_t current;
  2588. schlr_table *ptr;
  2589. schlr_table *thread;
  2590. int prev;
  2591. int next;
  2592.  
  2593. current = time(0) + lSeconds; //init wakeup time
  2594.  
  2595. if (head == EMPTY)
  2596. { //no thread delayed
  2597. head = nRunning;
  2598. next = EMPTY;
  2599. }
  2600. else //scan delayed threads
  2601. {
  2602. prev = EMPTY;
  2603. next = head;
  2604. while ((next != EMPTY) && (current >=
  2605. (ptr = (table + next))->wakeuptime))
  2606. {
  2607. prev = next;
  2608. next = ptr->next;
  2609. }
  2610. if (prev == EMPTY)
  2611. head = nRunning;
  2612. else
  2613. (table + prev)->next = nRunning;
  2614. }
  2615.  
  2616.  
  2617. ptr = (table + nRunning);
  2618. ptr->wakeuptime = current;
  2619. ptr->next = next;
  2620.  
  2621. Switch(&waitQ);
  2622.  
  2623. }
  2624.  
  2625. /* Used to retrieve the status of a semaphore */
  2626. void Cschlr::GetSemStates(Csemq *sem, long &lCount, int &fWait)
  2627. {
  2628. if (sem->GetType() == CST_COUNT)
  2629. {
  2630. 1Count = sem->GetCount();
  2631. fWait = 0;
  2632. }
  2633. else
  2634. {
  2635. 1Count = 0;
  2636. fWait = 1;
  2637. }
  2638. }
  2639.  
  2640. /* Used by the main PeekMessage loop to multiplex the
  2641. cpu among a number of threads */
  2642. void Cschlr::Run()
  2643. {
  2644. if ((nRunning = readyQ.Dequeue()) != CSC_NO_THREAD)
  2645. task[MAIN]->Transfer(*task[nRunning]);
  2646. else
  2647. WakeUp();
  2648. }
  2649.  
  2650. // End of File
  2651.  
  2652.  
  2653. Listing 3 Header file for class Csemq
  2654. #ifndef _CSEMQ
  2655. #define _CSEMQ
  2656.  
  2657. enum csemq_type {CST_COUNT, CST_QUEUE};
  2658. const int CSC_NO_THREAD = 31;
  2659. const long CSC_IDLE = 0x80000000L;
  2660.  
  2661. class Csemq
  2662. {
  2663. private:
  2664. long sem;
  2665. int priority;
  2666.  
  2667. Csemq();
  2668. Csemq(long Value);
  2669. int Dequeue();
  2670. void Enqueue(int nThread);
  2671. void UpdateCount(long 1Value);
  2672. csemq_type GetType();
  2673. long GetCount ();
  2674.  
  2675. friend Cschlr;
  2676.  
  2677. };
  2678.  
  2679. #endif
  2680.  
  2681. // End of File
  2682.  
  2683.  
  2684. Listing 4 Member function definitions for class Csemq
  2685. #include <stdlib.h>
  2686. #include "csemq.hpp"
  2687.  
  2688. /* Constructor */
  2689. Csemq::Csemq()
  2690. {
  2691. sem = 0L;
  2692. priority = CSC_NO_THREAD;
  2693. }
  2694.  
  2695. /* Another Constructor */
  2696. Csemq::Csemq(long lValue)
  2697. {
  2698. sem = 1Value;
  2699. priority = CSC_NO_THREAD;
  2700. }
  2701.  
  2702. /* Used to return a thread for execution based on a
  2703. round-robin-like scheduling algorithm */
  2704. int Csemq::Dequeue()
  2705. {
  2706. int nTask = CSC_NO_THREAD;
  2707. long task;
  2708.  
  2709. long tmp;
  2710. int i;
  2711.  
  2712. if (sem < 0)
  2713. {
  2714.  
  2715. //this implements a somewhat round-robin scheduling algorithm
  2716. task = _lrotl(1, priority + 1);
  2717. tmp = sem & ~CSC_IDLE;
  2718.  
  2719. /* scan the semaphore queue structure bit-by-bit */
  2720. for (i = 0; i < CSC_NO_THREAD + 1; i++)
  2721. {
  2722. if (task & tmp)
  2723. break;
  2724. else
  2725. task = _lrotl(task, 1);
  2726. }
  2727.  
  2728. sem &= ~task;
  2729. if (sem == CSC_IDLE)
  2730. sem = 0;
  2731.  
  2732. if (i <= CSC_NO_THREAD)
  2733. nTask = long (i + priority + 1) % 32L;
  2734.  
  2735. }
  2736.  
  2737.  
  2738. priority = nTask;
  2739.  
  2740. return (nTask);
  2741. }
  2742.  
  2743. /* Used to put a thread in the semaphore queue */
  2744. void Csemq::Enqueue(int nThread)
  2745. {
  2746. if ((nThread <= CSC_NO_THREAD) (sem <= 0))
  2747. {
  2748. sem = (1L << nThread) CSC_IDLE ;
  2749. }
  2750. }
  2751.  
  2752. /* Used to update the semaphore count by the specified
  2753. amount */
  2754. void Csemq::UpdateCount(long lValue)
  2755. {
  2756. long lTmp;
  2757.  
  2758. if (sem >= 0)
  2759. {
  2760. lTmp = sem + lValue;
  2761. if (lTmp >= 0)
  2762. sem = lTmp;
  2763. }
  2764. }
  2765.  
  2766. /* Used to return an indicator to the caller telling
  2767. it if the semaphore structure is a counter or a queue */
  2768. csemq_type Csemq::GetType()
  2769. {
  2770. csemq_type nType = CST_COUNT;
  2771.  
  2772. if (sem < 0)
  2773. nType = CST_QUEUE;
  2774.  
  2775. return (nType);
  2776. }
  2777.  
  2778. /* Used to get the count of a semaphore */
  2779. long Csemq::GetCount()
  2780. {
  2781. return (sem);
  2782. }
  2783.  
  2784. // End of File
  2785.  
  2786.  
  2787. Listing 5 Header file for class Cthread
  2788. #ifndef __CTHREAD
  2789. #define __CTHREAD
  2790.  
  2791. #include <setjmp.h>
  2792.  
  2793. const int THD_MAX_STACK = 0x4000; /* maximum stack size allowed */
  2794.  
  2795. typedef void (* THDFN)(void);
  2796.  
  2797.  
  2798. typedef struct thd_type {
  2799. jmp_buf Context; /* context of thread */
  2800. int TotalLen; /* total length of structure */
  2801. int Overflowed; /* overflow guard */
  2802. int Stack[THD_MAX_STACK]; /* stack of thread */
  2803. } THD, *THDPTR;
  2804.  
  2805. class Cthread
  2806. {
  2807. private:
  2808.  
  2809. THDPTR threadbody;
  2810.  
  2811. Cthread(THDFN func, int stacksize, int *frame, int framesize);
  2812.  
  2813. void Transfer(Cthread& thread);
  2814.  
  2815. ~Cthread(void);
  2816.  
  2817. friend Cschlr;
  2818.  
  2819. };
  2820.  
  2821. #endif
  2822.  
  2823. // End of File
  2824.  
  2825.  
  2826. Listing 6 Member function definitions for class Cthread
  2827. #include <stdlib.h>
  2828. #include <stdio.h>
  2829. #include "cthread.hpp"
  2830.  
  2831. /* This module is compiler-specific. It relies on the
  2832. structure of jmp_buf and the C function prolog and
  2833. epilog to work properly */
  2834.  
  2835. /* The following definitions are for the Zortech V3.0
  2836. C++Windows and OS/2 compilers */
  2837.  
  2838. #define WORDSTOREMOVE 3 //used for stack adjustment
  2839. typedef struct //jmp_buf map
  2840. {
  2841. long ip; //CS:IP
  2842. short filler1;
  2843. long sp; //SS:SP
  2844. } *mapptr;
  2845.  
  2846. /* Used to transfer to the specified thread. The context
  2847. of the running thread is saved in the thread object's
  2848. private data area */
  2849. void Cthread::Transfer(Cthread &thread)
  2850. {
  2851.  
  2852. /* don't transfer control if the new thread's
  2853. stack is corrupt */
  2854. if ((thread.threadbody == NULL) 
  2855. (thread.threadbody->Overflowed)) return;
  2856.  
  2857.  
  2858. /* save old thread's context and transfer control */
  2859. if (!setjmp(this->threadbody->Context))
  2860. longjmp(thread.threadbody->Context, 1);
  2861.  
  2862. }
  2863.  
  2864. /* Used to create a thread object and copy a number of
  2865. parameters to the thread's stack */
  2866. Cthread::Cthread(THDFN func, int stacksize,
  2867. int *frame, int framesize)
  2868. {
  2869.  
  2870. int i;
  2871. mapptr ptr;
  2872.  
  2873. /* allocate stack */
  2874. stacksize /= sizeof(int); //convert to out allocation unit
  2875. stacksize = (stacksize > THD_MAX_STACK) ? THD_MAX_STACK: stacksize;
  2876.  
  2877. threadbody = (THDPTR) new char[i = (sizeof(THD) - sizeof(int) *
  2878. (THD_MAX_STACK - stacksize))];
  2879. threadbody->Overflowed = 0;
  2880. threadbody->TotalLen = i;
  2881. if (threadbody == NULL)
  2882. {
  2883. printf("Thread creation failed...\n");
  2884. }
  2885.  
  2886. setjmp(threadbody->Context); //initialize jmp_buf structure
  2887. ptr = (mapptr) &threadbody->Context;
  2888. /* initialize stack with parameters if any */
  2889. if (stacksize)
  2890. {
  2891. if ((frame != NULL) && (stacksize > framesize))
  2892. {
  2893. for (i = 0; i < framesize; i++)
  2894. threadbody->Stack[stacksize - framesize + i] =
  2895. *frame++;
  2896. stacksize -= framesize;
  2897. }
  2898. //set stack pointer
  2899.  
  2900. ptr ->sp = (long) &(threadbody->Stack[stacksize -
  2901. WORDSTOREMOVE]);
  2902. }
  2903.  
  2904. /* set up the start of the thread body */
  2905. ptr->ip = (long) func;
  2906.  
  2907. }
  2908.  
  2909. /* Destructor */
  2910. Cthread::~Cthread(void)
  2911. {
  2912. delete [threadbody->TotalLen] (char *) threadbody;
  2913. threadbody = NULL;
  2914. }
  2915.  
  2916.  
  2917. // End of File
  2918.  
  2919.  
  2920. Listing 7 Implements main logic of example program BIGJOB.cpp
  2921. /* define externals and prototypes */
  2922. #include "cschlr.hpp"
  2923.  
  2924. VOID Timer(void);
  2925. VOID Big(void);
  2926. VOID PaintWindow( PGPFPARMS pGpfParms);
  2927. VOID SelectRep( PGPFPARMS pGpfParms);
  2928. VOID StartSalvage( PGPFPARMS pGpfParms);
  2929. VOID AbortSalvage( PGPFPARMS pGpfParms);
  2930. VOID CreateThreads(VOID);
  2931. VOID PeekMessageLoop(MSG * pMsg, Cschlr *pTask);
  2932. static double Savage(double x);
  2933.  
  2934. enum Status {IDLE, RUNNING, DONE, ABORTED};
  2935.  
  2936. SHORT idCommand = ID_REP10; //iteration command
  2937. short nIterations; //no. of iterations
  2938. Status status = IDLE; //current status
  2939. SHORT rep[4] = {10, 100, 1000, 10000}; //iteration values
  2940. time_t lStart; //start time
  2941. time_t lTime; //command total time
  2942. BOOL fContinue; //continue flag
  2943.  
  2944. int thdtimer, thdbig; //thread objects
  2945. Csemq *semjob, *semtimer; //semaphore objects
  2946. const int STACKSIZE = 4096; //stack size
  2947. Cschlr mtask; //scheduler object
  2948.  
  2949. /* Used to execute the Salvage benchmark once */
  2950. static double Savage(double x)
  2951. {
  2952. return tan(atan(exp(log(sqrt(x * x))))) + 1.0;
  2953. }
  2954.  
  2955. /* Used to diaply the main window to reflect the state of executeion */
  2956. VOID PaintWindow( PGPFPARMS pGpfParms)
  2957. {
  2958. static char *szFormat[]: {
  2959. "Idling...",
  2960. "Running... %ld seconds passed",
  2961. "Completed in %ld seconds: %d repetitions",
  2962. "Aborted after %ld seconds"
  2963. };
  2964.  
  2965. RECT rect;
  2966. char szMsg[50];
  2967.  
  2968. GetClientRect(pGpfParms->hwnd, &rect);
  2969. wsprintf(szMsg, szFormat[status], lTime, nIterations);
  2970. DrawText(pGpfParms->hdc, szMsg, -1, &rect,
  2971. DT_SINGLELINE DT_CENTER DT_VCENTER);
  2972.  
  2973. }
  2974.  
  2975. /* Timer thread function to display the status report
  2976.  
  2977. every 5 seconds */
  2978. VOID Timer(VOID)
  2979. {
  2980. int i;
  2981.  
  2982. while (1)
  2983. {
  2984. mtask.Wait(semtimer);
  2985. i = 0;
  2986. while (1)
  2987. {
  2988. mtask.Sleep(1L);
  2989. i++;
  2990. if (status == RUNNING)
  2991. {
  2992. if (i % 5 == 0)
  2993. {
  2994. InvalidateRect(hwndMainWindow, NULL, TRUE);
  2995. lTime = time(0) - lStart;
  2996. }
  2997. }
  2998. else
  2999. break;
  3000. }
  3001. }
  3002. }
  3003.  
  3004. /* The thread to actually carry out the Salvage benchmark
  3005. for a number of iterations */
  3006. VOID Big(VOID)
  3007. {
  3008. double x;
  3009. int i;
  3010.  
  3011. while (1)
  3012. {
  3013. mtask.Wait(semjob);
  3014. for (i = 0; i < nIterations; i++)
  3015. {
  3016. if (!fContinue)
  3017. break;
  3018. x = Savage(1.0);
  3019. mtask.Preempt();
  3020. }
  3021. lTime = time(0) - lStart;
  3022. if (fContinue)
  3023. status = DONE;
  3024. else
  3025. status = ABORTED;
  3026. GpfMenuGray(hwndMainWindow,ID_START,FALSE);
  3027. GpfMenuGray(hwndMainWindow,ID_ABORT,TRUE);
  3028. InvalidateRect(hwndMainWindow, NULL, TRUE);
  3029. }
  3030. }
  3031.  
  3032. /* Used to check mark the iteration count selected by the user */
  3033.  
  3034. VOID SelectRep( PGPFPARMS pGpfParms)
  3035. {
  3036.  
  3037.  
  3038. GpfMenuTick(hwndMainWindow,idCommand,FALSE);
  3039. idCommand = pGpfParms->Command;
  3040. GpfMenuTick(hwndMainWindow,pGpfParms->Command,TRUE);
  3041. return;
  3042.  
  3043. }
  3044.  
  3045. /* Used to start the Salvage benchmark by signalling the thread Big */
  3046. VOID StartSalvage( PGPFPARMS pGpfParms)
  3047. {
  3048.  
  3049. GpfMenuGray(hwndMainWindow, ID_ABORT,FALSE);
  3050. GpfMenuGray(hwndMainWindow, ID_START,TRUE);
  3051. nIterations = rep[idCommand - ID_REP10];
  3052. fContinue = TRUE;
  3053. time(&lStart);
  3054. status = RUNNING;
  3055. lTime = 0L;
  3056. InvalidateRect(hwndMainWindow, NULL, TRUE);
  3057.  
  3058. mtask.Signal(semjob);
  3059. mtask.Signal(semtimer);
  3060.  
  3061. return;
  3062.  
  3063. }
  3064.  
  3065. /* Used to abort the executing Salvage benchmark */
  3066. VOID AbortSalvage( PGPFPARMS pGpfParms)
  3067. {
  3068. fContinue = FALSE;
  3069. GpfMenuGray(hwndMainWindow, ID_START, FALSE);
  3070. GpfMenuGray(hwndMainWindow,ID_ABORT,TRUE);
  3071.  
  3072. return;
  3073.  
  3074. }
  3075.  
  3076. /* Used to create the Timer and Big threads */
  3077. VOID CreateThreads(VOID)
  3078. {
  3079. /* create mutlitasking environment */
  3080. thdtimer =mtask.CreateThread(Timer, STACKSIZE, NULL);
  3081. thdbig =mtask.CreateThread(Big, STACKSIZE, NULL);
  3082. semjob = mtask.CreateSem(0);
  3083. semtimer = mtask.CreateSem(0);
  3084. GpfMenuGray(hwndMainWindow, ID_ABORT,TRUE);
  3085. GpfMenuTick(hwndMainWindow, ID_REP10,TRUE);
  3086. }
  3087.  
  3088. /* Main PeekMessage loop to replace the normal
  3089. GetMessage loop */
  3090. VOID PeekMessageLoop(MSG * pMsg, Cschlr *pTask)
  3091. {
  3092.  
  3093. BOOL fGo = TRUE;
  3094.  
  3095. while (fGo)
  3096.  
  3097. {
  3098. if (pTask != NULL)
  3099. {
  3100. while (!PeekMessage(pMsg,NULL,NULL,NULL,PM_NOREMOVE))
  3101. pTask->Run();
  3102. }
  3103.  
  3104. if (GetMessage(pMsg,NULL,NULL,NULL))
  3105. {
  3106. if ((!pMsg->hwnd) (!IsDialogMessage
  3107. (GetActiveWindow(),pMsg)))
  3108. {
  3109. TranslateMessage(pMsg);
  3110. DispatchMessage(pMsg);
  3111. }
  3112.  
  3113. }
  3114.  
  3115. else
  3116. fGo = FALSE;
  3117.  
  3118. }
  3119. }
  3120.  
  3121. // End of File
  3122.  
  3123.  
  3124. Listing 8 Makefile for program BIGJOB
  3125. # ************** bigjob Make File (.Mak) ***************
  3126. #
  3127. # Generated by GPF (Gui Programming Facility) V1.3 Level(01)
  3128. #
  3129. # Program Name : bigjob
  3130. # DataBase Name : No
  3131. # Date and Time : Tue May 25 20:50:32 1993
  3132. # Program Level : 1.0
  3133. # Copyright : Andy Yuen 1993
  3134. #
  3135. #
  3136. **********************************************************
  3137.  
  3138. WinOptions = -2 -a1 -c -g -Ju -ml -W2 -x -Jm
  3139. DefOptions = -c -a1 -Ju -mli
  3140. CC = ztc
  3141. AllObj = \
  3142. bigjob.Obj csemq.obj cthread.obj cschlr.obj
  3143.  
  3144. # note that Cschlr, Csemq and Cthread should not be compiled to
  3145. # generate Windows function prologs and epilogs
  3146.  
  3147. # the following inference rule is for Cschlr Csemq and Cthread
  3148. .cpp.obj:
  3149.  
  3150. $(CC) $(DefOptions) $<
  3151.  
  3152. bigjob.exe: $(AllObj) bigjob.res
  3153. link @bigjob.l
  3154. rc bigjob.res bigjob.exe
  3155.  
  3156.  
  3157. bigjob.obj: bigjob.cpp bigjob.h
  3158. $(CC) $(WinOptions) bigjob.cpp
  3159.  
  3160. bigjob.Res: bigjob.Rc
  3161. Rc -r bigjob.Rc
  3162.  
  3163. # End of File
  3164.  
  3165.  
  3166.  
  3167.  
  3168.  
  3169.  
  3170.  
  3171.  
  3172.  
  3173.  
  3174.  
  3175.  
  3176.  
  3177.  
  3178.  
  3179.  
  3180.  
  3181.  
  3182.  
  3183.  
  3184.  
  3185.  
  3186.  
  3187.  
  3188.  
  3189.  
  3190.  
  3191.  
  3192.  
  3193.  
  3194.  
  3195.  
  3196.  
  3197.  
  3198.  
  3199.  
  3200.  
  3201.  
  3202.  
  3203.  
  3204.  
  3205.  
  3206.  
  3207.  
  3208.  
  3209.  
  3210.  
  3211.  
  3212.  
  3213.  
  3214.  
  3215.  
  3216.  
  3217.  
  3218.  
  3219.  
  3220. Standard C
  3221.  
  3222.  
  3223. Technical Corrigendum 1
  3224.  
  3225.  
  3226.  
  3227.  
  3228. P.J. Plauger
  3229.  
  3230.  
  3231. P.J. Plauger is senior editor of The C Users Journal. He is convenor of the
  3232. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  3233. latest books are The Standard C Library, and Programming on Purpose (three
  3234. volumes), all published by Prentice-Hall. You can reach him at
  3235. pjp@plauger.com.
  3236.  
  3237.  
  3238.  
  3239.  
  3240. Introduction
  3241.  
  3242.  
  3243. No sooner had the ANSI C Standard hit the streets in 1989 but people started
  3244. asking questions about it. Some questions were simple requests for
  3245. enlightenment, or how to interpret an apparent ambiguity or oversight. Others
  3246. were direct challenges to the correctness or completeness of the C Standard
  3247. itself. In each case, ANSI rules required that committee X3J11 respond to the
  3248. query as a Request For Interpretation, or RFI.
  3249. X3J11 did its best to respond to four dozen such RFIs. Unfortunately, the
  3250. rules of the game did not permit us to easily change the wording of the C
  3251. Standard. We often had to excuse what we wrote instead of simply making it a
  3252. bit clearer. Equally unfortunately, those dozens of RFIs and the committee
  3253. responses were slow to see the light of day. The first batch was only recently
  3254. approved by ANSI for publication.
  3255. Meanwhile, ISO committee JTC1-/SC22/WG14 assumed more and more responsibility
  3256. for the C Standard. It didn't help that the ISO rules for interpreting and
  3257. fixing language standards were, in their own way, as obscure and inappropriate
  3258. as ANSI's. It was not until the August 1992 plenary meeting of SC22 that we
  3259. laid down sensible procedures for responding to queries and challenges from
  3260. the public.
  3261. WGI4, with lots of cooperation from X3J11, has worked hard for the past year
  3262. or so to catch up. We've now processed nearly five dozen Defect Reports (or
  3263. DRs), the ISO analog to RFIs. All of the responses to date have been gathered
  3264. into a Record of Responses (or RR) that is now being balloted within SC22. By
  3265. the time you read these words, the ballot should be closed. I'll be astonished
  3266. if the ballot yields any serious opposition. Too many experts from too many
  3267. countries have labored for too many years for any major objections to remain
  3268. hidden.
  3269. A companion document to the Record of Responses, called a Technical
  3270. Corrigendum (or TC), summarizes all the changes that WG14 now recommends to
  3271. the ISO C Standard. The RR is not normative but the TC is. Most of the changes
  3272. are designed to clarify wording that can be misread. A few resolve ambiguities
  3273. or patch holes that are hard to argue away. Just one or two definitely change
  3274. the rules of C -- to make the language more like what X3J11 meant instead of
  3275. what we ended up saying. None of the changes add significant new features to
  3276. Standard C, or take any away.
  3277. What I present here are the actual instructions for changing the ISO C
  3278. Standard (ISO/IEC 9899:1990). Be warned that they are in draft form -- they
  3279. can still change in response to comments from the balloting. I expect such
  3280. changes to be small, however. I have extracted changes to the appendixes and
  3281. put them at the end. Otherwise, the changes occur in no particular page order.
  3282. That's the way we responded to the questions as they came in.
  3283. If you have the ANSI version instead (ANSI X3.159:1989), you'll find the
  3284. leading digit of the subclause number differs. Usually, the ISO number is
  3285. three higher. You'll also find that page numbers tend to be off by one,
  3286. mostly. But even if you lack a copy of the C Standard in any form, you should
  3287. make sense out of what follows.
  3288. I've added my own commentary in italics to explain the reason for each change.
  3289. The actual TC reproduces the Defect Report that led to the change, but I lack
  3290. the space to do the same here. The words in boldface are the meta-instructions
  3291. from the TC describing where each change should occur.
  3292.  
  3293.  
  3294. The Changes
  3295.  
  3296.  
  3297. Some implementors wanted to avoid copying structures in a function returning a
  3298. structure, even if that meant the return value might overlap a structure
  3299. argument value. We wanted to clarify that this is not permissible:
  3300. Add to subclause 6.6.6.4, page 80:
  3301. The overlap restriction in subclause 6.3.16.1 does not apply to the case of
  3302. function return.
  3303. Example
  3304. In:
  3305. struct s {double i;} f(void);
  3306.  
  3307. union {struct {int f1;
  3308. struct s f2;} u1;
  3309. struct {struct s f3;
  3310. int f4;} u2;
  3311. } g;
  3312.  
  3313. struct s f(void)
  3314. {
  3315. return g.u1.f2;
  3316. }
  3317.  
  3318. /* ... */
  3319.  
  3320. g.u2.f3 = f();
  3321. the behavior is defined.
  3322. We missed one or two places where the C grammar is ambiguous. Sometimes it's
  3323. hard to tell from context whether a type definition is being used a different
  3324. way in a nested scope. We generalized the guideline originally laid down by
  3325. Dennis Ritchie:
  3326. In subclause 6.5.4.3, page 68, change:
  3327. In a parameter declaration, a single typedef name in parentheses is taken to
  3328. be an abstract declarator that specifies a function with a single parameter,
  3329. not as redundant parentheses around the identifier for a declarator.
  3330.  
  3331. to:
  3332. If, in a parameter declaration, an identifier can be treated as a typedef name
  3333. or as a parameter name, it shall be taken as a typedef name.
  3334. We got the words wrong regarding two declarations for the same name. We meant
  3335. to have the later declaration assume the composite type of the two, even if
  3336. the earlier declaration was in an outer scope. This is a substantive change to
  3337. make Standard C behave as we intended:
  3338. In subclause 6.1.2.6, page 25, change:
  3339. For an identifier with external or internal linkage declared in the same scope
  3340. as another declaration for that identifier, the type of the identifier becomes
  3341. the composite type.
  3342. to:
  3343. For an identifier with internal or external linkage declared in a scope in
  3344. which a prior declaration of that identifier is visible*, if the prior
  3345. declaration specifies internal or external linkage, the type of the identifier
  3346. at the latter declaration becomes the composite type. [*Footnote: As specified
  3347. in 6.1.2.1, the latter declaration might hide the prior declaration.]
  3348. Here is a similar error regarding the determination of storage class. We meant
  3349. the rule to apply across any two scopes, not just file scope and another one,
  3350. so we fixed it:
  3351. In subclause 6.1.2.2, page 21, change:
  3352. If the declaration of an identifier for an object or a function contains the
  3353. storage-class specifier extern, the identifier has the same linkage as any
  3354. visible declaration of the identifier with file scope. If there is no visible
  3355. declaration with file scope, the identifier has external linkage.
  3356. to:
  3357. For an identifier declared with the storage-class specifier extern in a scope
  3358. in which a prior declaration of that identifier is visible*, if the prior
  3359. declaration specifies internal or external linkage, the linkage of the
  3360. identifier at the latter declaration becomes the linkage specified at the
  3361. prior declaration. If no prior declaration is visible, or if the prior
  3362. declaration specifies no linkage, then the identifier has external linkage.
  3363. [*Footnote: As specified in 6.1.2.1, the latter declaration might hide the
  3364. prior declaration.]
  3365. We wanted to clarify how a tentative array definition with unknown size gets
  3366. completed. Adding an example changes no normative wording, but provides a
  3367. useful hint to the reader:
  3368. Add to subclause 6.7.2, page 84:
  3369. Example
  3370. If at the end of the translation unit containing
  3371. int i[];
  3372. the array i still has incomplete type, the array is assumed to have one
  3373. element. This element is initialized to zero on program startup.
  3374. We wanted to clarify that array arguments become pointer arguments rather
  3375. early in the life of a function prototype. You can treat arrays as pointers
  3376. both for determining type compatibility and for forming a composite type:
  3377. In subclause 6.5.4.3, page 68, lines 23-25, change the two occurrences of:
  3378. its type for these comparisons
  3379. to:
  3380. its type for compatibility comparisons, and for determining a composite type.
  3381. A similar confusion recurs on just when a structure type becomes complete. We
  3382. clarified that completion occurs at the closing brace in the structure
  3383. definition:
  3384. In subclause 6.5.2.3, page 62, line 27, change:
  3385. occurs prior to the declaration that defines the content
  3386. to:
  3387. occurs prior to the } following the struct-declaration-list that defines the
  3388. content
  3389. Yet another confusion recurs about when the size of an enumeration is known:
  3390. Add to subclause 6.5.2.3, page 63:
  3391. Example
  3392. An enumeration type is compatible with some integral type. An implementation
  3393. may delay the choice of which integral type until all enumeration constants
  3394. have been seen. Thus in:
  3395. enum f { c = sizeof(enum f)};
  3396. the behavior is undefined since the size of the respective enumeration type is
  3397. not known when sizeof is encountered.
  3398. Some people read the description of fscanf as requiring a conversion failure
  3399. on %n when the input is exhausted. That was not our intent:
  3400. Add to subclause 7.9.6.2, page 138:
  3401. Example
  3402. In:
  3403. #include <stdio.h>
  3404.  
  3405. /* ... */
  3406.  
  3407. int d1, d2, n1, n2, i;
  3408.  
  3409. i = sscanf("123", "%d%n%n%d", &d1, &n1, &n2, &d2);
  3410. the value 123 is assigned to d1 and the value 3 to n1. Because %n can never
  3411. get an input failure the value of 3 is also assigned to n2. The value of d2 is
  3412. not affected. The value 3 is assigned to i.
  3413. We made clearer just what is meant by the implicit initialization of static
  3414. objects to zero:
  3415. In subclause 6.5.7, pages 71-72, change:
  3416. If an object that has static storage duration is not initialized explicitly,
  3417. it is initialized implicitly as if every member that has arithmetic type were
  3418. assigned 0 and every member that has pointer type were assigned a null pointer
  3419. constant.
  3420. to:
  3421. If an object that has static storage duration is not initialized explicitly,
  3422. it is initialized implicitly according to these rules:
  3423. if it has pointer type, it is initialized implicitly to a null pointer
  3424. constant;
  3425. if it has arithmetic type, it is initialized implicitly to zero;
  3426. if it is an aggregate, every member is initialized (recursively) according to
  3427. these rules;
  3428. if it is a union, the first named member is initialized (recursively)
  3429. according to these rules.
  3430. It was not completely clear that a newline always ends a preprocessing
  3431. directive:
  3432. Add to subclause 6.8, page 86, Description:
  3433. A new-line character ends the preprocessing directive even if it occurs within
  3434. what would otherwise be an invocation of a function-like macro.
  3435. Some situations in Standard C are described as both constraint violations and
  3436. undefined or implementation-defined behavior. We decided to clarify the
  3437. precedence of errors:
  3438.  
  3439. Add to subclause 5.1.1.3, page 6:
  3440. If a construct violates a constraint and is also specified as having undefined
  3441. or implementation-defined behavior the constraint takes precedence.
  3442. Example
  3443. An implementation shall issue a diagnostic for the translation unit:
  3444. char i;
  3445. int i;
  3446. because in those cases where wording in this International Standard describes
  3447. the behavior for a construct as being both a constraint error and resulting in
  3448. undefined behavior, the constraint error shall be diagnosed.
  3449. Some people felt it was not obvious enough that the members of a structure or
  3450. union inherit its storage class:
  3451. Add to subclause 6.5.1, page 58:
  3452. A declaration of an aggregate or union with a storage-class specifier other
  3453. than typedef implicitly causes all of its members to be given the
  3454. storage-class specifier.
  3455. We wanted to clarify that assignment to a narrower type does indeed
  3456. effectively stuff the value through a knothole, scraping off high-order bits:
  3457. Add to subclause 6.3.16.1, page 54:
  3458. Example
  3459. In the fragment:
  3460. char c;
  3461. int i;
  3462. long l;
  3463.  
  3464. l = ( c = i );
  3465. the value of i is converted to the type of the assignment-expression c = i,
  3466. that is, char type. The value of the expression enclosed in parenthesis is
  3467. converted to the type of the outer assignment-expression, that is, long type.
  3468. Some people were confused about the meaning of "ignored" when talking about
  3469. unnamed structure or union members during initialization:
  3470. In subclause 6.5.7, page 71, line 39, change:
  3471. All unnamed structure or union members are ignored during initialization.
  3472. to:
  3473. Except where explicitly stated otherwise, for the purposes of this subclause
  3474. unnamed members of objects of struct and union type do not participate in
  3475. initialization. Unnamed members of struct objects have indeterminate value
  3476. even after initialization. A union containing only unnamed members has
  3477. indeterminate value even after initialization.
  3478. In subclause 6.5.7, page 72, lines 4-5, change:
  3479. The initial value of the object is that of the expression:
  3480. to:
  3481. The initial value of the object, including unnamed members, is that of the
  3482. expression:
  3483. How macros get expanded is a source of confusion to many. We added yet another
  3484. example to help clarify this difficult topic:
  3485. Add to subclause 6.8.3.3, page 90:
  3486. Example
  3487. #define hash_hash # ## #
  3488. #define mkstr(a) # a
  3489. #define in_between(a) mkstr(a)
  3490. #define join(c, d) in_between(c hash_hash d)
  3491.  
  3492. char p[] = join(x, y);
  3493. /* equivalent to char p[] = "x ## y"; */
  3494. The expansion produces, at various stages:
  3495. join(x, y)
  3496.  
  3497. in_between(x hash_hash y)
  3498.  
  3499. in_between(x ## y)
  3500.  
  3501. mkstr(x ## y)
  3502.  
  3503. "x ## y"
  3504. In other words, expanding hash_hash produces a new token, consisting of two
  3505. adjacent sharp-signs, but this new token is not the catenation operator.
  3506. Here's a one-word change, to clarify that we are talking about identifiers in
  3507. general and not some (unspecified) one in particular:
  3508. In subclause 7.1.2, page 96, lines 34-35, change:
  3509. However, if the identifier is declared or defined in more than one header,
  3510. to:
  3511. However, if an identifier is declared or defined in more than one header,
  3512. The functions ftell and fgetpos can often fail. Only values returned by
  3513. successful calls are permitted in certain contexts:
  3514. In subclause 7.9.9.2, page 145, lines 39-40, change:
  3515. a value returned by an earlier call to the ftell function
  3516. to:
  3517.  
  3518. a value returned by an earlier successful call to the ftell function
  3519. In subclause 7.9.9.3, page 146, lines 10-11, change:
  3520. a value obtained from an earlier call to the fgetpos function
  3521. to:
  3522. a value obtained from an earlier successful call to the fgetpos function
  3523. We really didn't say clearly what is the type of a function call expression:
  3524. In subclause 6.3.2.2, page 40, line 35, change:
  3525. The value of the function call expression is specified in 6.6.6.4.
  3526. to:
  3527. If the expression that denotes the called function has type pointer to
  3528. function returning an object type, that object type is the type of the result
  3529. of the function call. The value of the function call is determined by the
  3530. return statement that executes within the called function, as specified in
  3531. 6.6.6.4. Otherwise, the function call has type void.
  3532. We used two different terms for "iteration structures" and "control
  3533. structures." This change eliminates the form we used only once:
  3534. In subclause 5.2.4.1, page 13, lines 1-2, change:
  3535. -- 15 nested levels of compound statements, iteration control structures, and
  3536. selection control structures
  3537. to:
  3538. -- 15 nested levels of compound statements, iteration statements, and
  3539. selection statements
  3540. Some readers insisted on believing that an expression such as: x<3&&0>x must
  3541. be parsed to include the token <3&&0>, and hence requires a diagnostic. It was
  3542. easier to add a sentence to the C Standard than to continue to fight such
  3543. perversity:
  3544. Add to subclause 6.1, page 18:
  3545. There is one exception to this rule: a header-name preprocessing token is only
  3546. recognized within a #include preprocessing directive, and within such a
  3547. directive, a sequence of characters that could be either a header-name or a
  3548. string-literal is recognized as the former.
  3549. Here is a similar, but milder, form of the same perversity:
  3550. Add to subclause 6.1.2, page 20:
  3551. When preprocessing tokens are converted to tokens during translation phase 7,
  3552. if a preprocessing token could be converted to either a keyword or an
  3553. identifier, it is converted to a keyword.
  3554. More cleanup of header-name parsing:
  3555. In subclause 6.1.7, page 32, delete:
  3556. Constraint
  3557. Header name preprocessing tokens shall only appear within a #include
  3558. preprocessing directive.
  3559. Add to subclause 6.1.7, page 32:
  3560. The header-name preprocessing token is recognized only within a #include
  3561. preprocessing directive.
  3562. The %0 conversion specifier in fprintf has some subtle implications. It is not
  3563. the same as forcing a zero fill. Nor is it the same as forcing increased
  3564. precision:
  3565. In subclause 7.9.6.1, page 132, lines 37-38, change:
  3566. For 0 conversion, it increases the precision to force the first digit of the
  3567. result to be a zero.
  3568. to:
  3569. For 0 conversion, it increases the precision, if and only if necessary, to
  3570. force the first digit of the result to be a zero.
  3571. Similarly, the matching rules for fscanf seem to need no end of clarification:
  3572. In subclause 7.9.6.2, page 135, change:
  3573. An input item is defined as the longest matching sequence of input characters,
  3574. unless that exceeds a specified field width, in which case it is the initial
  3575. subsequence of that length in the sequence.
  3576. to:
  3577. An input item is defined as the longest sequence of input characters which
  3578. does not exceed any specified field width and which is, or is a prefix of, a
  3579. matching input sequence.
  3580. In subclause 7.9.6.2, page 137, delete:
  3581. If conversion terminates on a conflicting input character, the offending input
  3582. character is left unread in the input stream.
  3583. Add to subclause 7.9.6.2, page 137:
  3584. fscanf pushes back at most one input character onto the input stream.*
  3585. Therefore, some sequences that are acceptable to strtod, strtol, or strtoul
  3586. are acceptable to fscanf. [*Footnote: If conversion terminates on a
  3587. conflicting input character, the offending input character is left unread in
  3588. the input stream.]
  3589. The following change started out in an entirely different arena. We wanted to
  3590. clarify that an implementation can add extra identifier characters, such as $,
  3591. provided that it issues a diagnostic when they're used. But we discovered an
  3592. ambiguity in how such extra characters would parse in a macro definition. So
  3593. we decided to resolve the ambiguity and make the extension more usable:
  3594. Add to subclause 6.8, page 86, Constraints:
  3595. If the first character of a replacement-list is not a member of the minimal
  3596. basic source character set*, there shall be white-space separation between the
  3597. identifier and the replacement-list. [*Footnote: "Minimal basic source
  3598. character set" refers to the 90-odd basic source characters listed in
  3599. subclause 5.2.1.]
  3600. We thought it was clear enough that library macros should be written sensibly,
  3601. but not everyone seemed to agree:
  3602. Add to subclause 7.1.2, page 96:
  3603. Any definition of a macro described in this clause shall expand to code that
  3604. is fully protected by parentheses where necessary, so that it groups in an
  3605. arbitrary expression as if it were a single identifier.
  3606. Here's a small but potentially misleading gaffe in an example:
  3607. Change subclause 7.12.2.3, page 172, line 16, from:
  3608. if (mktime(&time_str) == -1)
  3609. to:
  3610. if (mktime(&time_str) ==
  3611. (time_t)-1)
  3612. And a similar error in the index:
  3613. In the index, page 217, change:
  3614. static storage-class specifier, 3.1.2.2, 6.1.2.4, 6.5.1, 6.7
  3615. to:
  3616. static storage-class specifier, 6.1.2.2, 6.1.2.4, 6.5.1, 6.7
  3617. When we listed the rules for aliasing (accessing the same object by lvalues
  3618. with different types), we were overly restrictive in describing the kinds of
  3619. qualified types that are valid:
  3620.  
  3621. In subclause 6.3, page 38, lines 18-21, change:
  3622. An object shall have its stored value accessed only by an lvalue expression
  3623. that has one of the following types:36
  3624. -- the declared type of the object,
  3625. -- a qualified version of the declared type of the object,
  3626. to:
  3627. An object shall have its stored value accessed only by an lvalue expression
  3628. that has one of the following types: 36
  3629. -- a type compatible with the declared type of the object,
  3630. -- a qualified version of a type compatible with the declared type of the
  3631. object,
  3632. Some of the functions declared in <string.h> take a length argument, which can
  3633. be zero. We spelled out what happens when that argument is zero:
  3634. Add to subclause 7.11.1, page 162:
  3635. Where an argument declared as size_t n determines the length of the array for
  3636. a function, n can have the value zero on a call to that function. Unless
  3637. explicitly stated otherwise in the description of a particular function,
  3638. pointer arguments on such a call must still have valid values, as described in
  3639. subclause 7.1.7 Use of library functions. On such a call, a function that
  3640. copies characters shall copy zero characters, while a function that compares
  3641. two character sequences shall return zero.
  3642. We made clear that the macros for signal numbers defined in <signal. h> must
  3643. have distinct values:
  3644. In subclause 7.7, page 120, lines 14-16, change:
  3645. and the following, each of which expands to a positive integral constant
  3646. expression that is the signal number corresponding to the specified condition:
  3647. to:
  3648. and the following, which expand to positive integral constant expressions with
  3649. distinct values that are the signal numbers, each corresponding to the
  3650. specified condition:
  3651.  
  3652.  
  3653. Listing Undefined Behavior
  3654.  
  3655.  
  3656. The UK delegation to WG14 wants a complete list of undefined behaviors in
  3657. Appendix G.2. This is part of an ongoing effort to round out that list:
  3658. Add to subclause G.2, page 204:
  3659. -- A program contains no function called main.
  3660. Add to subclause G.2, page 204:
  3661. -- A storage-class specifier or type-qualifier modifies the keyword void as a
  3662. function parameter-type-list.
  3663. Add to subclause G.2, page 204:
  3664. -- For an array of arrays, the permitted pointer arithmetic in subclause
  3665. 6.3.6, page 47, lines 12-40 is to be understood by interpreting the use of the
  3666. word "object" as denoting the specific object determined directly by the
  3667. pointer's type and value, not other objects related to that one by contiguity.
  3668. Therefore, if an expression exceeds these permissions, the behavior is
  3669. undefined. For example, the following code has undefined behavior:
  3670. int a[4][5];
  3671.  
  3672. a[1][7] = 0; /* undefined */
  3673. Some conforming implementations may choose to diagnose an "array bounds
  3674. violation," while others may choose to interpret such attempted accesses
  3675. successfully with the "obvious" extended semantics.
  3676. Add to subclause G.2, page 204:
  3677. -- If a fully expanded macro replacement list contains a function-like macro
  3678. name as its last pre-processing token, it is unspecified whether this macro
  3679. name may be subsequently replaced. If the behavior of the program depends upon
  3680. this unspecified behavior, then the behavior is undefined.
  3681. Example
  3682. Given the definitions:
  3683. #define f(a) a*g
  3684. #define g(a) f(a)
  3685. the invocation:
  3686. f(2)(9)
  3687. results in undefined behavior. Among the possible behaviors are the generation
  3688. of the preprocessing tokens:
  3689. 2*f(9)
  3690. and
  3691. 2*9*g
  3692. Add to subclause G.2, page 204:
  3693. -- A call to a library function exceeds an Environmental limit.
  3694.  
  3695.  
  3696. Conclusion
  3697.  
  3698.  
  3699. I believe these changes are reasonably minor. The C Standard is honored by
  3700. dozens of vendors, required by hundreds of customers, and validated by several
  3701. agencies around the world. Yet it has seen remarkably few challenges for all
  3702. that. Put another way, the C Standard has held up pretty well these past five
  3703. years.
  3704. Still, it doesn't hurt to fix the obvious flaws. The fewer ambiguities in a
  3705. document, the fewer misunderstandings result. And, of course, the fewer
  3706. questions get directed to those of us who generate answers.
  3707.  
  3708.  
  3709.  
  3710.  
  3711.  
  3712.  
  3713.  
  3714.  
  3715.  
  3716.  
  3717.  
  3718.  
  3719.  
  3720.  
  3721.  
  3722.  
  3723.  
  3724.  
  3725.  
  3726.  
  3727.  
  3728.  
  3729.  
  3730.  
  3731.  
  3732.  
  3733.  
  3734.  
  3735.  
  3736.  
  3737.  
  3738.  
  3739.  
  3740.  
  3741.  
  3742.  
  3743.  
  3744.  
  3745.  
  3746.  
  3747.  
  3748.  
  3749.  
  3750.  
  3751.  
  3752.  
  3753.  
  3754.  
  3755.  
  3756.  
  3757.  
  3758.  
  3759.  
  3760.  
  3761.  
  3762.  
  3763.  
  3764.  
  3765.  
  3766.  
  3767.  
  3768.  
  3769.  
  3770.  
  3771.  
  3772.  
  3773.  
  3774.  
  3775.  
  3776.  
  3777. Stepping Up To C++
  3778.  
  3779.  
  3780. How Virtual Functions Work
  3781.  
  3782.  
  3783.  
  3784.  
  3785. Dan Saks
  3786.  
  3787.  
  3788. Dan Saks is the founder and principal of Saks & Associates, which offers
  3789. consulting and training in C++ and C. He is secretary of the ANSI and ISO C++
  3790. committees. Dan is coauthor of C++ Programming Guidelines, and codeveloper of
  3791. the Plum Hall Validation Suite for C++ (both with Thomas Plum). You can reach
  3792. him at 393 Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601,
  3793. or electronically at dsaks@wittenberg.edu.
  3794.  
  3795.  
  3796. Last month, I introduced virtual functions. (See "Stepping Up to C++: Virtual
  3797. Functions," CUJ, December 1993). I outlined how you might use virtual
  3798. functions to implement a device-independent file system, and showed in detail
  3799. how to create a class of geometric shapes with polymorphic behavior. I
  3800. continue this month by explaining the mechanics of how virtual functions work.
  3801. But first, a brief recap of the key concepts.
  3802. Public inheritance in C++ defines an Is-A relationship between a derived class
  3803. and its base. That is, given the definition
  3804. class D : public B { ... };
  3805. D is publicly derived from B, and any D object is also a B object. A function
  3806. that expects a pointer (or reference) to a B object as its formal parameter
  3807. will accept a pointer (or reference, respectively) to a D object as well.
  3808. More generally, converting a pointer to a D object into a pointer to a B
  3809. object is a standard conversion that does not require a cast. For example, if
  3810. d is an object of class D and B is a public base class of D you can write
  3811. B *pb = &d;
  3812. which converts &d (an expression of type D *) to B *. Binding a D object to a
  3813. B & is also a standard conversion.
  3814. When discussing the behavior of pointers to base and derived objects, it helps
  3815. to distinguish the static type of an object from its dynamic type. The static
  3816. type of an object is the type of the expression used to refer to that object.
  3817. The dynamic type of an object is its "actual" type -- the object's type at the
  3818. point where it was created.
  3819. For example, using pb as defined and initialized above, *pb has static type B
  3820. but dynamic type D. Or, given
  3821. B &rb = d;
  3822. rb has static type B but dynamic type D. The static type of *pb is always B,
  3823. but its dynamic type may change during program execution. For example, if b is
  3824. a B object, then
  3825. pb = &b;
  3826. changes the dynamic type of *pb to B.
  3827. A derived class inherits all the members of its base class. A derived class
  3828. cannot discard any members it inherits, but it can override the definition of
  3829. an inherited member function with a new definition.
  3830. In C++, non-static member functions are non-virtual by default. C++ resolves
  3831. non-virtual member function calls by static binding. That is, if pb is
  3832. declared as a B *, and B has a non-virtual member function , then pb->f always
  3833. calls B's f. Even if, at the time of the call, pb actually points to a D
  3834. object (where D is derived from B and overrides ), calling pb->f still invokes
  3835. B's f, not D's f.
  3836. On the other hand, virtual member function calls bind dynamically. If pb is a
  3837. B * and f is declared as a virtual member function in B, then calling pb->f
  3838. calls f associated with the dynamic type of *pb. Thus, if pb actually points
  3839. to a B object, then pb->f calls B's f. But, if pb actually points to a D
  3840. object (where D is derived from B and overrides f), then pb->f calls D's f.
  3841. A class with at least one virtual member function is called a polymorphic
  3842. type, and objects of such type exhibit polymorphism. Polymorphism lets you
  3843. define a single interface for a hierarchy of subtypes that exhibit logically
  3844. similar, but physically different, behavior. Using polymorphism, you can pass
  3845. pointers or references to objects of a derived class type to functions that
  3846. know the object only by its base class type. Yet, the object retains its
  3847. dynamic type so that member function calls applied to that object invoke the
  3848. derived class behavior.
  3849. Listing 1 shows the class definition for shape, a polymorphic class for
  3850. geometric shapes that I described last month. Listing 2 shows the
  3851. corresponding member function and static data member definitions. Class shape
  3852. has three virtual functions, area, name and put, and two non-virtual
  3853. functions, color and shape (a constructor). Listing 3 and Listing 4 show the
  3854. complete definitions for classes circle and rectangle, respectively, both
  3855. derived from shape. Each derived class defines it own constructor
  3856. (constructors are not inherited), and also overrides each of its inherited
  3857. virtual functions with appropriate definitions.
  3858. Listing 5 contains a function that illustrates the power of polymorphism.
  3859. Function largest locates the shape with the largest area from a collection of
  3860. shapes. Since shape is a polymorphic type, calling sa[i]->area returns the
  3861. area of a shape without the caller ever knowing exactly what kind of shape
  3862. *sa[i] really is.
  3863.  
  3864.  
  3865. vptrs and vtbls
  3866.  
  3867.  
  3868. Both the ARM (Ellis and Stroustrup [1990]) and the emerging C++ standard take
  3869. pains to describe the behavior of virtual functions, as well as the rest of
  3870. the C++ language, without suggesting any particular implementation strategy.
  3871. However, the ARM does describe implementation techniques in the commentary at
  3872. the end of chapter 10 on derived classes. I find that relying on a model
  3873. implementation simplifies the description of many details of the behavior of
  3874. virtual functions. The following is one such model.
  3875. Typical C++ implementations add a single pointer to each object of a
  3876. polymorphic class. That pointer is called a vptr (pronounced "VEE-pointer").
  3877. Whenever a constructor for a polymorphic class initializes an object, its sets
  3878. that object's vptr to the address of a table of function pointers called a
  3879. vtbl ("VEE-table"). Each entry in the vtbl is the address of a virtual
  3880. function. All objects of a given class share the same vtbl; that vtbl contains
  3881. exactly one entry for each virtual function in that class.
  3882. For example, Figure 1 shows the layout for an object of class shape (as
  3883. defined in Listing 1) along with its corresponding vtbl. Every shape object
  3884. has the same two fields in the same order: a vptr and a _colo data member. The
  3885. vptr points to shape's vtbl, which contains the addresses of the virtual
  3886. functions shape: :area, shape::name and shape::put. The non-virtual functions
  3887. shape::color and shape::shape (the constructor) don't use any space in the
  3888. vtbl, nor in the object itself.
  3889. Figure 2 and Figure 3 show the layouts for circle and rectangle objects,
  3890. respectively (as defined in Listing 3 and Listing 4, respectively), along with
  3891. their corresponding vtbls. Notice that the initial portions of both circle
  3892. objects and rectangle objects are shape objects, so a pointer to a circle or a
  3893. rectangle is a pointer to a shape, and the conversion from circle * or
  3894. rectangle * to shape * requires no pointer arithmetic.
  3895. The vtbls for both derived classes have their function pointers in the same
  3896. order as the vtbl for the base class, although the pointer values differ. For
  3897. example, the vtbl entry for the area function is always first in every class
  3898. derived (directly or indirectly) from shape. The vtbl entry for name is always
  3899. second, and the entry for put is always third.
  3900. Whereas a non-virtual function call generates a call instruction that refers
  3901. directly to the function's address as determined during translation (compiling
  3902. and linking), a virtual function call generates additional code to locate the
  3903. function's address in the vtbl.
  3904. The ARM suggests viewing a vtbl as an array of function addresses, so that
  3905. each call locates the called function by subscripting into the vtbl. For
  3906. example, if ps is a pointer to a shape, then
  3907. a = ps->area ( );
  3908. translates into something like
  3909. a = (*(ps->vtbl[0]))(ps);
  3910. and
  3911. ps->put(cout);
  3912. translates into
  3913. (*(ps->vtbl[2]))(ps, cout);
  3914. An expression of the form ps->vtbl [n] is the th entry in the vtbl of the
  3915. object *ps, so (*(ps->vtbl [n])) is the nth virtual function itself. Actually,
  3916. in C++ as in C, you need not explicitly dereference a pointer to a function in
  3917. a call expression. Thus you can write
  3918. (*(ps->vtbl[2])) (ps, cout);
  3919. as simply
  3920. (ps->vtbl[2])(ps, cout);
  3921. Each virtual function may have a different signature (sequence of formal
  3922. parameter types) and return type. So strictly speaking, you can't implement
  3923. vtbls as arrays of pointers because an array requires all its elements to have
  3924. the same type. For example, shape::area has type double (*)() and shape::put
  3925. has type void (*)(ostream &).
  3926.  
  3927. I prefer to model a vtbl as a struct in which all the members are pointers to
  3928. functions. For instance, you can define the struct type for a vtbl for shapes
  3929. as something like
  3930. struct shape_virtual_table
  3931. {
  3932. double (*area)();
  3933. const char *(*name)();
  3934. ostream &(*put)(ostream &os);
  3935. };
  3936. and define the actual shape vtbl as something like
  3937. shape_virtual_table shape_vtbl =
  3938. {
  3939. &shape::area,
  3940. &shape::name,
  3941. &shape::put
  3942. };
  3943. Similarly, you can define the circle vtbl as something like
  3944. shape_virtual_table circle_vtbl =
  3945. {
  3946. &circle::area,
  3947. &circle::name,
  3948. &circle::put
  3949. };
  3950. (I say "something like" because this code won't actually compile. The code
  3951. only demonstrates the general layout of the vtbls.) Using this translation
  3952. model,
  3953. a = ps->area();
  3954. translates into
  3955. a = (*ps->vtbl->area)(ps);
  3956. or simply
  3957. a = ps->vtbl->area(ps);
  3958. and
  3959. ps->put(cout);
  3960. translates into
  3961. (*ps->vtbl->put)(ps, cout);
  3962. or just
  3963. ps->vtbl->put(ps, cout);
  3964. A virtual function call with n arguments translates into a call (through a
  3965. vtbl entry) with n+1 arguments. The additional argument is always the address
  3966. of the object to which function applies; in the examples above, its value is
  3967. always ps. The additional argument becomes the value of this inside the called
  3968. function. Virtual functions cannot be static members, so they always have an
  3969. implicit this argument.
  3970. Bear in mind that I'm describing only a typical implementation strategy.
  3971. (Colvin [1993] describes a similar implementation of virtual method tables
  3972. generated with the aid of macros that support object-oriented programming in
  3973. C.) C++ translators may implement virtual functions somewhat differently, but
  3974. the effect must be the same. The vptr need not be at the beginning of each
  3975. polymorphic object. But, for any class D derived from polymorphic class B, D's
  3976. vptr must be at the same offset within D as B's vptr is within B. Similarly,
  3977. the function pointers in the vtbl need not be in the same order as the virtual
  3978. function declarations in the class. But, for any D derived from a polymorphic
  3979. B, the initial portion of D's vtbl must have the same layout as B's vtbl, even
  3980. if D's actual pointer values differ from B's because D has overridden some of
  3981. the virtual functions it inherited.
  3982. In short, a C++ translator must insure that the base subobject of any derived
  3983. object has the same layout as any other object of the same base type, and the
  3984. base portion of a derived class vtbl must have the same layout as the base
  3985. class vtbl. Hence, a translator need not see the declarations for any derived
  3986. classes when translating a virtual call. Regardless of p's dynamic type, a
  3987. virtual call such as p->f always translates into code to
  3988. construct f's actual argument list
  3989. follow p's vptr to a vtbl
  3990. transfer control to the function whose address is in the vtbl entry
  3991. corresponding to f.
  3992. All polymorphic objects with a given dynamic type can share the same physical
  3993. vtbl. Some C++ implementations actually manage to eliminate duplicate vtbls.
  3994. Others produce multiple copies of vtbls, either due to limitations of the
  3995. development environment or to provide better system performance. Many
  3996. implementations offer compiler and linker options to let you decide.
  3997. This implementation model shows that virtual functions introduce a small cost
  3998. in both space and time:
  3999. Adding one or more virtual functions to a class that previously had none adds
  4000. a vptr to each object of that class.
  4001. Each polymorphic class adds at least one more vtbl to the program's data
  4002. space.
  4003. Every constructor for a polymorphic class must initialize a vptr.
  4004. Every virtual function call must locate the address of the function by looking
  4005. in a vtbl (requiring typically 2 to 4 additional machine instructions).
  4006. In C++, member functions are non-virtual by default because C++ tries to
  4007. adhere to the principle that "you don't pay for what you don't use." If you're
  4008. willing to pay for a virtual function call, you must say so explicitly.
  4009.  
  4010.  
  4011. Selective Overriding
  4012.  
  4013.  
  4014. A derived class may override all, some, or none of the virtual functions in
  4015. its base class. A derived class inherits the function definitions for all
  4016. virtual functions it does not override. Listing 6 and Figure 4 together
  4017. illustrate the effects of selectively overriding only some of the virtual
  4018. functions inherited from a base class.
  4019. Listing 6 shows a simple class hierarchy and Figure 4 shows the corresponding
  4020. vtbls. Class B defines three virtual functions, f, g, and h. Class C derived
  4021. from B overrides only function f, so the entries for g and h in C's vtbl
  4022. continue to point to B's g and h. Class D derived from C overrides only
  4023. function h, so the entries for f and g in D's vtbl are the same as in C's
  4024. vtbl. Since neither C nor D overrides g, all three vtbl's have the same value
  4025. for g's entry, namely B's g.
  4026. In Listing 6, pc has static type C *. But by the time program execution
  4027. reaches the declaration
  4028. B &rb = *pc;
  4029. pc has dynamic type D *. Thus all the calls applied to rb use D's vtbl.
  4030.  
  4031.  
  4032. Pure Virtual Functions
  4033.  
  4034.  
  4035.  
  4036. Sometimes when you design a type hierarchy, you find that you don't want to
  4037. create the hierarchy's base class object. Rather, the base class serves only
  4038. as the specification for a common interface for objects of types derived from
  4039. it.
  4040. For example, in a hierarchy that implements a device-independent file system
  4041. (such as the one I sketched in my last column), the base class file defines
  4042. the properties common to all file types. The derived classes define specific
  4043. file types, like disk_file or tape_file, that are types for the real-live file
  4044. objects in the system. But there is no such thing as a file that is not a
  4045. disk_file, or a tape_file, or some other device-specific file. The base class
  4046. only specifies the common file interface.
  4047. In my shape hierarchy, I never really wanted to create objects whose dynamic
  4048. type is shape. Class shape is only supposed to define the common properties
  4049. for shapes. I implemented class shape so that objects of that type appear to
  4050. be points, not because I needed a shape that's a point, but because I hadn't
  4051. yet presented a way to avoid defining the function bodies for virtual
  4052. functions. In fact, with early C++ dialects, you had no choice but to define
  4053. phony function bodies. Now you can simply declare the functions as pure
  4054. virtual functions.
  4055. You declare a function as a pure virtual function by adding the pure virtual
  4056. specifier = 0 at the end of the function declaration. Listing 7 shows the
  4057. definition for class shape with area, name, and put as pure virtual functions.
  4058. A base class with at least one pure virtual function is called an abstract
  4059. base class. You cannot declare objects of an abstract base class. However, you
  4060. can declare pointers and references to an abstract base class:
  4061. shape s; // error
  4062. void f(shape *ps); // ok
  4063. shape &rs; // ok
  4064. If a derived class does not override every pure virtual function in the base
  4065. class with an "impure" virtual function, then the derived class is also an
  4066. abstract base class. For example, class D in Listing 8 is an abstract base
  4067. class because it fails to override pure virtual function g with a function
  4068. definition.
  4069. In the model implementation, the vtbl entry for a pure virtual function is a
  4070. null pointer.
  4071. References
  4072. Colvin [1993]. Gregory Colvin. "Extending C for Object-Oriented Programming,"
  4073. The C Users Journal, Vol. 11, No. 7, July 1993.
  4074. Ellis and Stroustrup [1990]. Margaret A. Ellis and Bjarne Stroustrup. The
  4075. Annotated C++ Reference Manual. Addison- Wesley.
  4076. Figure 1 Layout of class shape
  4077. Figure 2 Layout of class circle
  4078. Figure 3 Layout of class rectangle
  4079. Figure 4 Selectively overriding only some virtual functions
  4080.  
  4081. Listing 1 A base class for shapes
  4082. class shape
  4083. {
  4084. public:
  4085. enum palette { BLUE, GREEN, RED };
  4086. shape(palette c);
  4087. virtual double area() const;
  4088. virtual const char *name() const;
  4089. virtual ostream &put(ostream &os) const;
  4090. palette color() const;
  4091. private:
  4092. palette_color;
  4093. static const char *color_image[RED - BLUE + 1];
  4094. };
  4095.  
  4096. inline ostream &operator<<(ostream &os, const shape &s)
  4097. {
  4098. return s.put(os);
  4099. }
  4100.  
  4101. // End of File
  4102.  
  4103.  
  4104. Listing 2 Member function and static member data definitions for class shape
  4105. shape::shape(palette c) : _color(c) { }
  4106.  
  4107. shape::palette shape::color( ) const
  4108. {
  4109. return _color;
  4110. }
  4111.  
  4112. double shape::area() const
  4113. {
  4114. return 0;
  4115. }
  4116.  
  4117. const char *shape::name() const
  4118. {
  4119. return "point";
  4120.  
  4121. }
  4122.  
  4123. ostream &shape::put(ostream &os) const
  4124. {
  4125. return os << color_image[_color] << '' << name();
  4126. }
  4127.  
  4128. const char *shape::color_image[shape::RED - shape::BLUE + 1] =
  4129. { "blue", "green", "red" };
  4130.  
  4131. // End of File
  4132.  
  4133.  
  4134. Listing 3 Class circle derived form shape
  4135. class circle : public shape
  4136. {
  4137. public:
  4138. circle(palette c, double r);
  4139. double area() const;
  4140. const char *name() const;
  4141. ostream &put(ostream &os) const;
  4142. private:
  4143. double radius;
  4144. };
  4145.  
  4146. circle::circle(palette c, double r) : shape(c), radius(r) { }
  4147.  
  4148. double circle::area() const
  4149. {
  4150. const double pi = 3.1415926;
  4151. return pi * radius * radius;
  4152. }
  4153.  
  4154. const char *circle::name() const
  4155. {
  4156. return "circle";
  4157. }
  4158.  
  4159. ostream &circle::put(ostream &os) const
  4160. {
  4161. return shape::put(os) << "with radius = " << radius;
  4162. }
  4163.  
  4164. // End of File
  4165.  
  4166.  
  4167. Listing 4 Class rectangle derived from shape
  4168. class rectangle : public shape
  4169. {
  4170. public:
  4171. rectangle(palette c, double h, double w);
  4172. double area() const;
  4173. const char *name() const;
  4174. ostream &put(ostream &os) const;
  4175. private:
  4176. double height, width;
  4177. };
  4178.  
  4179. rectangle::rectangle(palette c, double h, double w)
  4180.  
  4181. : shape(c), height(h), width(w) { }
  4182.  
  4183. double rectangle::area() const
  4184. {
  4185. return height * width;
  4186. }
  4187.  
  4188. const char *rectangle::name() const
  4189. {
  4190. return "rectangle";
  4191. }
  4192.  
  4193. ostream &rectangle::put(ostream &os) const
  4194. {
  4195. return shape::put(os) << " with height = " << height
  4196. << " and width = " << width;
  4197. }
  4198.  
  4199. // End of File
  4200.  
  4201.  
  4202. Listing 5 A function that returns the shape with the largest area in a
  4203. collection of shapes
  4204. const shape *largest(const shape *sa[], size_t n)
  4205. {
  4206. const shape *s = 0;
  4207. double m = 0;
  4208. double a;
  4209. for (size_t i = 0; i < n; ++i)
  4210. if ((a = sa[i]->area()) > m)
  4211. {
  4212. m = a;
  4213. s = sa[i];
  4214. }
  4215. return s;
  4216. }
  4217.  
  4218. // End of File
  4219.  
  4220.  
  4221. Listing 6 Selective virtual overriding
  4222. #include <iostream.h>
  4223.  
  4224. class B
  4225. {
  4226. public:
  4227. virtual void f();
  4228. virtual void g();
  4229. virtual void h();
  4230. };
  4231.  
  4232. class C : public B
  4233. {
  4234. public:
  4235. void f(); // virtual
  4236. };
  4237.  
  4238. class D : public C
  4239. {
  4240. public:
  4241.  
  4242. void h(); // virtual
  4243. };
  4244.  
  4245. void B::f() { cout << "B::f()\n"; }
  4246.  
  4247. void B::g() { cout << "B::g()\n"; }
  4248.  
  4249. void B::h() { cout << "B::h()\n"; }
  4250.  
  4251. void C::f() { cout << "C::f()\n"; }
  4252.  
  4253. void D::h() { cout << "D::h()\n"; }
  4254.  
  4255. int main()
  4256. {
  4257. C c;
  4258. D d;
  4259.  
  4260. B *pb = &c; // ok, &c is a C * which is a B *
  4261. pb->f(); // calls C::f()
  4262. pb->g(); // calls B::g()
  4263. pb->h(); // calls B::h()
  4264.  
  4265. C *pc = &d; // ok, &d is a D * which is a C *
  4266. pc->f(); // calls C::f()
  4267. pc->g(); // calls B::g()
  4268. pc->h(); // calls D::h()
  4269.  
  4270. B &rb = *pc; // ok, *pc is a C which is a B
  4271. rb.f(); // calls C::f()
  4272. rb.g(); // calls B::g()
  4273. rb.h(); // calls D::h()
  4274.  
  4275. return 0;
  4276. }
  4277.  
  4278. // End of File
  4279.  
  4280.  
  4281. Listing 7 An abstract base class for shapes
  4282. class shape
  4283. {
  4284. public:
  4285. enum palette { BLUE, GREEN, RED };
  4286. shape(palette c);
  4287. virtual double area() const = 0;
  4288. virtual const char *name() const = 0;
  4289. virtual ostream &put(ostream &os) const = 0;
  4290. palette color() const;
  4291. private:
  4292. palette _color;
  4293. static const char *color_image[RED - BLUE + 1];
  4294. };
  4295.  
  4296. inline ostream &operator<<(ostream &os, const shape &s)
  4297. {
  4298. return s.put(os);
  4299. }
  4300.  
  4301.  
  4302. // End of File
  4303.  
  4304.  
  4305. Listing 8 Derived class that doesn't override all pure virtual functions is
  4306. still abstract
  4307. class B
  4308. {
  4309. public:
  4310. virtual void f();
  4311. virtual void g() = 0;
  4312. };
  4313.  
  4314. void B::f() { ... }
  4315.  
  4316. class D : public B
  4317. {
  4318. public:
  4319. void f(); // virtual
  4320. // g is still pure virtual
  4321. };
  4322.  
  4323. void D::f() { ... }
  4324.  
  4325. int main()
  4326. {
  4327. B b; // error, B is abstract class
  4328. D d; // error, D is abstract class
  4329. ...
  4330. }
  4331.  
  4332. // End of File
  4333.  
  4334.  
  4335.  
  4336.  
  4337.  
  4338.  
  4339.  
  4340.  
  4341.  
  4342.  
  4343.  
  4344.  
  4345.  
  4346.  
  4347.  
  4348.  
  4349.  
  4350.  
  4351.  
  4352.  
  4353.  
  4354.  
  4355.  
  4356.  
  4357.  
  4358.  
  4359.  
  4360.  
  4361.  
  4362.  
  4363.  
  4364.  
  4365.  
  4366. Questions and Answers
  4367.  
  4368.  
  4369. Lint for C++
  4370.  
  4371.  
  4372.  
  4373.  
  4374. Kenneth Pugh
  4375.  
  4376.  
  4377. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++
  4378. language courses for corporations. He is the author of C Language for
  4379. Programmers and All On C, and was a member of the ANSI C committee. He also
  4380. does custom C programming for communications, graphics, image databases, and
  4381. hypertext. His address is 4201 University Dr., Suite 102, Durham, NC 27707.
  4382. You may fax questions for Ken to (919) 489-5239. Ken also receives email at
  4383. kpugh@allen.com (Internet) and on Compuserve 70125,1142.
  4384.  
  4385.  
  4386. A few months ago, a reader asked a question regarding the availability of Lint
  4387. for C++. I replied that there was no product available at that time. In
  4388. response to the column, Gimpel software, makers of PC-lint, sent me a beta
  4389. copy of their new PC-lint for C/C++. I've used their C product on a number of
  4390. programs with great success in finding obscure bugs -- especially on programs
  4391. I inherited form other people. Their new version provides the same types of
  4392. error analysis for C++ programs as the older versions did for C programs.
  4393. The new version of PC-lint adds a number of C++ specific error messages.
  4394. PC-lint analyzes the relationship of class data and class member functions and
  4395. provides warnings about some common errors in class design. No existing
  4396. compiler that I know about flags these errors. For example, PC-lint can report
  4397. when a destructor for a base class is not virtual or does not exist. In this
  4398. situation, using a collection of base class pointers that contain pointers to
  4399. the derived classes will result in a call to the wrong destructor.
  4400. Another common mistake that PC-lint catches is the use of a constructor for a
  4401. class that calls new, without having a copy constructor or assignment operator
  4402. declared for the class. Normally a class that allocates memory requires the
  4403. declaration of an explicit copy constructor and an explicit assignment
  4404. operator. These member functions usually need to allocate memory, unless the
  4405. class implementation uses some form of reference counting.
  4406. The new version of PC-lint also adds a few more C warnings, such as the
  4407. warning issued for a missing semicolon at the end of a structure definition.
  4408. Heeding this warning would eliminate the relatively obscure compiler messages
  4409. that result from code such as the following:
  4410. struct my_struct
  4411. {
  4412. int member;
  4413. }
  4414. function(int x)
  4415. {
  4416. ...
  4417. }
  4418. An important purpose of the original lint program was to catch parameter type
  4419. mismatches. C++ has eliminated the need for this check by making function
  4420. prototypes mandatory, but it has added the potential for a number of problems
  4421. resulting from the misuse of classes. According to Gimpel Software, the list
  4422. of possible C++ warnings that are analyzed will expand in the next version.
  4423.  
  4424.  
  4425. A Discussion of Object Orientation
  4426.  
  4427.  
  4428. I just spent a week teaching an advanced C course. Most of the prepared
  4429. material covered syntax and construction of complex objects, but all of the
  4430. students were more interested in the design of objects.
  4431. Designing good classes is one of the most rewarding aspects of C++. Using bad
  4432. classes can be one of the most frustrating. Therefore, it's useful to develop
  4433. criteria for determining how well a class is designed.
  4434. The subjective measurements of a class's quality include coupling,
  4435. cohesiveness, sufficiency, primitiveness, and completeness. Coupling is the
  4436. degree of interdependence between objects. Classes that are friends of other
  4437. objects are highly coupled; classes that just depend on the existence of other
  4438. objects are loosely coupled.
  4439. In a highly cohesive class, the data and function members work together as a
  4440. single abstraction.
  4441. The last three criteria, sufficiency, primitiveness, and completeness. A class
  4442. should have a sufficient interface, which is one that provides all required
  4443. operations. The class needs to contain all primitive operations, which are
  4444. those requiring access to the hidden implementation. Finally, a class is
  4445. complete if it provides all possible operations that a user might want to
  4446. perform on it or with it. A class that is primative is easier to port to
  4447. another system or to modify, as it has fewer member functions than a complete
  4448. class. On the other hand, a complete class can be easier for users to employ
  4449. in their code.
  4450. An example is in order here. Suppose you created a file class called File. The
  4451. most primitive interface for this class might look like:
  4452. class File
  4453. {
  4454. public:
  4455. File(char * name, int mode);
  4456. ~File();
  4457. int read(char * buffer, int length);
  4458. int write(char * buffer, int length);
  4459. };
  4460. With this interface, the constructor might throw an exception if it could not
  4461. open the file. Alternatively, read and write might return the appropriate
  4462. error indication if the file could not be opened. The destructor will close
  4463. the file if the constructor was able to open it. For simplicity's sake, I used
  4464. an int for the constructor's mode parameter. You could use some type of
  4465. enumerated parameter (e.g. enum FILE_ACCESS_TYPE {READ_ONLY, WRITE_ONLY, ....}
  4466. ) instead for a clearer functional interface.
  4467. The following primitive interface is similar:
  4468. class File
  4469. {
  4470. public:
  4471. File();
  4472. ~File();
  4473. int open(char * name, int mode);
  4474. int read(char * buffer, int length);
  4475. int write(char * buffer, int length);
  4476. int close();
  4477.  
  4478. };
  4479. Notice, however, that the meaning of class File has changed. File no longer
  4480. represents a particular file. Now, an object of class File can be reused in
  4481. the same scope with a different file. The interface is still primitive. In
  4482. particular, there is no seek function to go to a particular byte in the file.
  4483. A user who needs to perform a seek, can use read and throw away the
  4484. intervening data. To get to a previous position in the file, close the file,
  4485. reopen it, and perform another read. Since seek can be implemented as a
  4486. combination of primitive operations, it is not a primitive operation.
  4487. Since many operating systems provide a seek operation, it would be more
  4488. efficient to include that as part of the interface. On systems that did not
  4489. have seek, the implementation could fake it with discardable reads. So a
  4490. slightly less primitive, but more efficient interface would look like:
  4491. class File
  4492. {
  4493. public:
  4494. File();
  4495. ...
  4496. long seek(long position, int direction);
  4497. };
  4498. As a side note, you could replace the long parameter with a typedef, as the
  4499. standard C fseek function uses. You could implement the direction parameter as
  4500. an enumeration.
  4501.  
  4502.  
  4503. Sufficiency
  4504.  
  4505.  
  4506. A typical user might want to find out the current file position. The primitive
  4507. interface does not provide an operation for determining file position. The
  4508. user could determine it by keeping track of the reads, writes, and seeks.
  4509. Since this facility is commonly needed, a sufficient interface could be
  4510. written as follows:
  4511. class File
  4512. {
  4513. public:
  4514. File();
  4515. ...
  4516. long seek(long position, int direction);
  4517. long tell();
  4518. };
  4519.  
  4520.  
  4521. Toward Completeness
  4522.  
  4523.  
  4524. Users may occasionally reposition files to the first byte, using seek(0, 0).
  4525. You might code a convenient macro for this operation as follows:
  4526. #define rewind() seek(0,0);
  4527. If rewind were a common operation, you might want to add the function to the
  4528. interface. The function is not necessary, but may be generally useful. Adding
  4529. a rewind function would make the interface more complete. The question is when
  4530. to stop adding functions. Should you also include a
  4531. search_for_a_byte_value(int byte_value)
  4532. function? Or should you let the user write a private version? The more member
  4533. functions a class contains, the more overwhelming it can be. The fewer
  4534. functions a class contains, the more code the user may have to write.
  4535.  
  4536.  
  4537. Objective Criteria for Class Design
  4538.  
  4539.  
  4540. A few implicit objective criteria for adding functions exist. First, an object
  4541. should perform the operations requested of it by the user. If the object
  4542. cannot perform an operation, it should notify the user through an error
  4543. return, an exception, or by some other means. Second, an object should do no
  4544. harm. Using an object should not cause memory to be overwritten, or cause
  4545. changes to other objects, unless those actions were truly intended. (Science
  4546. fiction buffs may notice that these criteria are similar to Isaac Asimov's
  4547. rules for robots.)
  4548. The following example demonstrates the need for these rules. I spent a few
  4549. days trying to use an object-oriented user interface generator for Microsoft
  4550. Windows, DOS, and other systems. The documentation for the system assumes that
  4551. you are new to C++, as it covers the language in some detail. On the other
  4552. hand, the system uses pointers and pointers to tables of pointers to objects
  4553. as part of the programmer interface.
  4554. One of the objects the system supports represents a vertical list. One of the
  4555. member functions for the vertical list object loads the object from persistent
  4556. storage. The member function must open a corresponding file to get the data
  4557. for the object. Unfortunately, the function failed to close that file. Not
  4558. even the destructor for the object closed the file.
  4559. In my application, this function error did not manifest itself until the
  4560. fourth time through a particular series of operations. The program ran fine
  4561. until it suddenly was unable to open a data file. I spent a number of hours to
  4562. determine what unapparent error on my part had caused such a problem. The
  4563. answer: my application had exceeded the open file limit; the object's storage
  4564. file had been repeatedly opened without ever being closed.
  4565. The failure of this object to obey one of the two "objective" rules (to not
  4566. cause harm) definitely caused a lot of human grief.
  4567.  
  4568.  
  4569. Questions and Answers
  4570.  
  4571.  
  4572.  
  4573.  
  4574. Pointer Types
  4575.  
  4576.  
  4577. Q
  4578. The C-program in Listing 1 generates an unexpected warning when compiled using
  4579. version 2.3.3 of gcc (no options specified). The diagnostic is:
  4580. test.c: In function 'main':
  4581. test.c:12: warning: passing arg 1 of 'sub2' from incompatible
  4582.  
  4583. pointer type
  4584. The diagnostic is tied to my prototype on line 7 where I declare a single
  4585. argument to be a pointer to an array of const int dimensioned DIM2. This
  4586. should prevent any obvious assignments to the parameter a within sub2, which
  4587. was my intention.
  4588. I've run this by several people (including gcc tech support) and some agree
  4589. with the compiler and others do not. I would appreciate your opinion.
  4590. Michael G. Soyka
  4591. Warren, RI
  4592. A
  4593. This is a great followup to a question from last month. In that issue, I
  4594. discussed the datatypes of variables such as:
  4595. int int_array2d[1][10];
  4596. The type of int_array_2d usually reduces to (int (*)[10]) and int_array
  4597. usually reduces to (int *), so:
  4598. pointer_to_int_array = int_array_2d;
  4599. pointer_to_int = int_array;
  4600. are compatible assignments. I use the phrase "usually reduces to" to emphasize
  4601. that the type of an array is not the same as a pointer type. Let's add a const
  4602. term to one set of these variables, as in the following:
  4603. const int int_array_of_const[1];
  4604. const int *pointer_to_int_const;
  4605. The type of int_array_of_const usually reduces to (const int *), so the
  4606. assignment of
  4607. pointer_to_int_const = int_array_of_const;
  4608. is proper. When using two-dimensionional arrays and pointers, typing becomes
  4609. somewhat more complex. For the declarations
  4610. const int int_array_2d_of_const[1][10];
  4611. const int (*pointer_to_array_of_const_int)[10];
  4612. the type of int_array_2d_of_const usually reduces to (const int (*)[10]), so
  4613. the assignment is compatible. Now, the reduced type of int_array_2d from the
  4614. first example was (int (*)[10]). That reduced type is not the same as (const
  4615. int (*)[10]), which is the type of pointer_to_array_of_const_int. So
  4616. pointer_to_int_array_of_const = int_array_2d;
  4617. yields a compiler warning, since these two expressions represent pointers to
  4618. incompatible types. One expression represents a pointer to a const array, the
  4619. other to a non-const array.
  4620. A pointer to an int can be assigned to a pointer to a const int. This
  4621. assignment simply adds "constness" to the object originally pointed at by the
  4622. pointer to int. The C Standard does not include an example of this operation,
  4623. but its effect seems to follow from the definition of const. The reverse
  4624. operation is not allowed without a cast, as that would be taking away
  4625. "constness."
  4626. It seems appropriate to me to allow a programmer to add "constness" to an
  4627. object at any level. I discussed this concept in a conversation with Chris
  4628. Skelly, who has written for The C User's Journal and has written a book (soon
  4629. to be published) on pointers. We agreed that it seems proper to be able to add
  4630. "constness" to a data type with an assignment.
  4631. However, according to P.J. Plauger, the C Standard does not require that a
  4632. pointer to array of const T be assignment-compatible with a pointer to array
  4633. of T. Some vendors may provide this latitude while others may not.
  4634. I tried your program with the Borland C++ 3.1 and Microsoft C++ 7.0 compilers.
  4635. Borland accepts it without complaint. Microsoft generates a message similar to
  4636. gcc. I altered your original example as shown in Listing 2 to better
  4637. illustrate the problem.
  4638. This program yielded an error reading:
  4639. error C2440: 'initializing' : cannot convert
  4640. from 'int [1][1]' to 'const int (_near *)[1]'
  4641. The preceding diagnostic is valid for the line
  4642. const int (*pointer_2d_to_const_2)[DIM2] = array_2d;
  4643. For reasons I explained before, the diagnostic may not be clearly worded,
  4644. although it is valid. Note that the compiler had no problem with
  4645. const int (*pointer_2d_to_const_1)[DIM2]: array_2d_of_const;
  4646. which implies that the conversion from const int [1][1] to const int (_near
  4647. *)[1] is acceptable.
  4648. Interestingly enough, the diagnostic does not appear when the program is
  4649. compiled as a C program with Microsoft. This discrepancy is probably due to
  4650. the more extensive type-checking performed by C++ compilers.
  4651. One way to solve your problem is shown in Listing 3. I use a typedef to
  4652. eliminate one dimension from the other declarations. The use of typedef makes
  4653. the array and pointers equivalent to the following declarations:
  4654. int int_array[1];
  4655. const int *pointer_to_const int;
  4656. Along these same lines, I ran into an "interesting" problem when I needed to
  4657. convert some old C code from the Microsoft large model to C++ under the medium
  4658. model. Nobody would do this if they want to maintain their sanity. However,
  4659. the requirement existed, so I plunged ahead. I did the conversion in two steps
  4660. -- first to large model C++, then to medium model C++.
  4661. The first step went relatively smoothly. I had already determined the
  4662. interface to the C++ objects and created test implementations of the objects.
  4663. I linked the overall test program using the test implementation and the
  4664. results came out correct. Then I began the second step. My addition of __far
  4665. to every pointer seemed to work fine. The compiler warned me about the few
  4666. places that I missed, except... And it was a big exception:
  4667. One of the original function prototypes was defined as follows:
  4668. function (char *array[])
  4669. array was an array of pointers to character strings. Since I could use either
  4670. pointer or array syntax for an argument, I would expect to be able to convert
  4671. the prototype to:
  4672. function (char __far * __far array[]);
  4673. I used the first __far because the array was going to contain elements which
  4674. were far pointers to char. The second __far was for the array itself. This
  4675. declaration should be logically equivalent to
  4676. function (char __far * __far * array);
  4677. In fact, Microsoft's compiler accepts the former declaration form without a
  4678. hint of a warning -- but the code compiles incorrectly. Garbage addresses are
  4679. passed. Memory is overwritten. Windows crash. Beeps go off. For the first time
  4680. in a long period, I had to invoke a debugger. With the switch to the second
  4681. form of the declaration all the problems went away.
  4682. This address problem occurs only with multiply-dimensioned arrays; it's
  4683. similar to your const problem. There appears to be a fundamental disagreement
  4684. in how to bind __far to arrays versus how to bind it to pointers. Since __far
  4685. is not in the C Standard, compiler vendors can do it any way they wish. I just
  4686. wish they would give me a warning.
  4687.  
  4688. Listing 1 Yields unexepected warning
  4689. 1: #define DIM1 10
  4690. 2: #define DIM2 10
  4691. 3:
  4692. 4: int a[DIM1][DIM2];
  4693. 5:
  4694. 6: void sub1 ( int (*a)[DIM2]);
  4695. 7: void sub2 (const int (*a)[DIM2]);
  4696. 8:
  4697. 9: void main ()
  4698.  
  4699. 10: {
  4700. 11: sub1 (a);
  4701. 12: sub2 (a);
  4702. 13: }
  4703.  
  4704. /* End of File */
  4705.  
  4706.  
  4707. Listing 2 Potentially incompatible assignments
  4708. #define DIM1 1
  4709.  
  4710. #define DIM2 1
  4711.  
  4712. int array_2d[DIM1][DIM2];
  4713. const int array_2d_of_const[DIM1][DIM2] = {{1}};
  4714.  
  4715. void function1 ()
  4716. {
  4717. int (*pointer_2d)[DIM2] = array_2d;
  4718. const int (*pointer_2d_to_const_1)[DIM2] = array_2d_of_const;
  4719. const int (*pointer_2d_to_const_2)[DIM2] = array_2d;
  4720. }
  4721.  
  4722. /* End of File */
  4723.  
  4724.  
  4725. Listing 3 Using a typedef to eliminate incompatibilities
  4726. #define DIM1 1
  4727. #define DIM2 1
  4728.  
  4729. typedef int INT_ARRAY_DIM2[DIM2];
  4730.  
  4731. INT_ARRAY_DIM2 array_2d[DIM1];
  4732. const INT_ARRAY_DIM2 array_2d_of_const[DIM1] = {{1}};
  4733.  
  4734. void function1 ()
  4735. {
  4736. INT_ARRAY_DIM2 *pointer_2d = array_2d;
  4737. const INT_ARRAY_DIM2 *pointer_2d_to_const_1 = array_2d_of_const;
  4738. const INT_ARRAY_DIM2 *pointer_2d_to_const_2 = array_2d;
  4739. }
  4740.  
  4741. /* End of File */
  4742.  
  4743.  
  4744.  
  4745.  
  4746.  
  4747.  
  4748.  
  4749.  
  4750.  
  4751.  
  4752.  
  4753.  
  4754.  
  4755.  
  4756.  
  4757.  
  4758.  
  4759.  
  4760.  
  4761.  
  4762. Code Capsules
  4763.  
  4764.  
  4765. Bit Handling in C++, Part 2
  4766.  
  4767.  
  4768.  
  4769.  
  4770. Chuck Allison
  4771.  
  4772.  
  4773. Chuck Allison is a regular columnist with CUJ and a software architect for the
  4774. Family History Department of the Church of Jesus Christ of Latter Day Saints
  4775. Church Headquarters in Salt Lake City. He has a B.S. and M.S. in mathematics,
  4776. has been programming since 1975, and has been teaching and developing in C
  4777. since 1984. His current interest is object-oriented technology and education.
  4778. He is a member of X3J16, the ANSI C++ Standards Committee. Chuck can be
  4779. reached on the Internet at allison@decus.org, or at (801)240-4510.
  4780.  
  4781.  
  4782.  
  4783.  
  4784. The bits Class Template
  4785.  
  4786.  
  4787. The standard C++ library has two classes for bit manipulation: bitstring and
  4788. bits. Last month I discussed the bitstring class, which defines objects that
  4789. behave like an array of bits that expands or contracts according to your
  4790. needs. This month's installment explains the bits class, an abstraction which
  4791. extends C's bitwise semantics by allowing easy access to bits, by allowing an
  4792. arbitrary (but fixed) number of bits in a bitset, and by adding important new
  4793. functionality. Following last month's format, I present here an excerpt from
  4794. the official proposal accepted by the standards committee, a working
  4795. implementation, and examples of how to use the class.
  4796. Class bits accommodates a fixed-length collection of bits. You can think of a
  4797. bits object as an arbitrarily large unsigned integer. It is actually a class
  4798. template, with the number of bits in the collection as the template parameter.
  4799. (See the sidebar "Templates in a Nutshell.") It is highly suitable for
  4800. interfacting with the host operating system, and is designed for efficiency.
  4801. (It can be stack-based.) Here's a sample program run on a machine with 16-bit
  4802. integers:
  4803. // tbits.cpp:
  4804. // Set some bits
  4805. // and display the result -
  4806. #include <iostream.h>
  4807. #include <stddef. h>
  4808. #include <limits.h>
  4809. #include "bits.h"
  4810.  
  4811. main()
  4812. {
  4813.  
  4814. const size_t SIZE
  4815. = CHAR_BIT * sizeof(int);
  4816. bits<SIZE> flags;
  4817. enum open_mode {in, out, ate,
  4818. app, trunc,
  4819. binary};
  4820.  
  4821. flags.set(in);
  4822. flags.set(binary);
  4823. cout << "flags:"
  4824. << flags <<" (0x"
  4825. << hex
  4826. << flags.to_ushort()
  4827. << ")\n";
  4828. cout << "binary?"
  4829. << (flags.test(binary)
  4830. ? "yes" : "no")
  4831. << endl;
  4832. return 0;
  4833. }
  4834.  
  4835. Output
  4836. flags: 0000000000100001 (0x21)
  4837. binary? yes
  4838.  
  4839.  
  4840.  
  4841. Member Function Descriptions
  4842.  
  4843.  
  4844. This section is a modified excerpt from the official proposal which describes
  4845. the semantics of each member function. For a quick look at the class
  4846. interface, see Listing 6. Since the library group of the joint C++ standards
  4847. committee is still deciding how to integrate exceptions into the standard
  4848. library, I just mention them briefly here. The names and uses of exceptions
  4849. are subject to change. I use asserts in place of exceptions in the
  4850. implementation (see Listing 7).
  4851.  
  4852.  
  4853. 1.0 Constructors
  4854.  
  4855.  
  4856.  
  4857.  
  4858. Synopsis
  4859.  
  4860.  
  4861. bits()
  4862. bits(unsigned long n)
  4863. bits(const string& s)
  4864. bits(const bits<N>& b)
  4865.  
  4866.  
  4867. 1.1 Constructor bits()
  4868.  
  4869.  
  4870.  
  4871.  
  4872. Description
  4873.  
  4874.  
  4875. Initializes all bits to zero.
  4876.  
  4877.  
  4878. 1.2 Constructor bits(unsigned long n)
  4879.  
  4880.  
  4881.  
  4882.  
  4883. Description
  4884.  
  4885.  
  4886. Initializes the object with the bits of n. If N >sizeof(unsigned long) *
  4887. CHAR_BIT, sets the extra bits to zero.
  4888.  
  4889.  
  4890. 1.3 Constructor bits(const string& s)
  4891.  
  4892.  
  4893.  
  4894.  
  4895. Description
  4896.  
  4897.  
  4898. Each character of s is interpreted as a bit (a string of 1s and 0s is
  4899. expected). In typical integral fashion, treats the last (right-most) character
  4900. of s as bit 0.
  4901.  
  4902.  
  4903. Exceptions
  4904.  
  4905.  
  4906. Throws invalid_argument if a character other than 1 or 0 is encountered.
  4907.  
  4908.  
  4909.  
  4910. 1.4 Constructor bits(const bits<N>& b)
  4911.  
  4912.  
  4913.  
  4914.  
  4915. Description
  4916.  
  4917.  
  4918. Standard copy constructor.
  4919.  
  4920.  
  4921. 2.0 Destructor
  4922.  
  4923.  
  4924. No destructor required.
  4925.  
  4926.  
  4927. 3.0 Other Member Functions
  4928.  
  4929.  
  4930.  
  4931.  
  4932. 3.1 Function unsigned short to_ushort() const
  4933.  
  4934.  
  4935.  
  4936.  
  4937. Description
  4938.  
  4939.  
  4940. Converts the n least significant bits of *this (where n == sizeof(unsigned
  4941. short) * CHAR_BIT) to an unsigned short. This is useful when the bits
  4942. represent flags in a word passed to the operating system.
  4943.  
  4944.  
  4945. Exceptions
  4946.  
  4947.  
  4948. Throws overflow if N > n and any of the bits above position n-1 are set.
  4949.  
  4950.  
  4951. 3.2 Function unsigned long to_ulong() const
  4952.  
  4953.  
  4954.  
  4955.  
  4956. Description
  4957.  
  4958.  
  4959. Converts the n least significant bits of *this (where n == sizeof(unsigned
  4960. long) * CHAR_BIT) to an unsigned long.
  4961.  
  4962.  
  4963. Exceptions
  4964.  
  4965.  
  4966. Throws overflow if N > n and any of the bits above position n-1 are set.
  4967.  
  4968.  
  4969. 3.3 Function string to_string() const
  4970.  
  4971.  
  4972.  
  4973.  
  4974.  
  4975. Description
  4976.  
  4977.  
  4978. Creates a string of 1s and 0s representing the contents of *this. As with
  4979. unsigned integers, treats the last character as bit 0.
  4980.  
  4981.  
  4982. Returns
  4983.  
  4984.  
  4985. The temporary string of 1s and 0s.
  4986.  
  4987.  
  4988. 3.4 Function bits<N>& operator:(const bits<N>& b)
  4989.  
  4990.  
  4991.  
  4992.  
  4993. Description
  4994.  
  4995.  
  4996. Standard assignment operator.
  4997.  
  4998.  
  4999. Returns
  5000.  
  5001.  
  5002. A reference to *this.
  5003.  
  5004.  
  5005. 3.5 Function int operator==(const bits<N>& b) const
  5006.  
  5007.  
  5008.  
  5009.  
  5010. Description
  5011.  
  5012.  
  5013. Compares *this to b for equality. Two bitsets are equal if and only if their
  5014. bit patterns are identical.
  5015.  
  5016.  
  5017. Returns
  5018.  
  5019.  
  5020. Non-zero if the bitsets are equal, zero otherwise.
  5021.  
  5022.  
  5023. 3.6 Function int operator!=(const bits<N>& b) const
  5024.  
  5025.  
  5026.  
  5027.  
  5028. Description
  5029.  
  5030.  
  5031. Compares *this to b for inequality. Equivalent to !operator==().
  5032.  
  5033.  
  5034.  
  5035. Returns
  5036.  
  5037.  
  5038. Zero if the bitsets are equal, non-zero otherwise.
  5039.  
  5040.  
  5041. 3.7 Functions set
  5042.  
  5043.  
  5044.  
  5045.  
  5046. Synopsis
  5047.  
  5048.  
  5049. bits<N>& set(size_t n, int val = 1)
  5050. bits<N>& set()
  5051.  
  5052.  
  5053. Description
  5054.  
  5055.  
  5056. These functions set one or more bits. The function set(size_t, int) can reset
  5057. a bit, depending on val.
  5058.  
  5059.  
  5060. 3.7.1 Function bits<N>& set[size_t n, int val)
  5061.  
  5062.  
  5063.  
  5064.  
  5065. Description
  5066.  
  5067.  
  5068. Sets the nth bit if val is non-zero, otherwise resets the bit.
  5069.  
  5070.  
  5071. Returns
  5072.  
  5073.  
  5074. A reference to *this.
  5075.  
  5076.  
  5077. Exceptions
  5078.  
  5079.  
  5080. Throws out_of_range if n is not in [0,N-1].
  5081.  
  5082.  
  5083. 3.7.2 Function bits<N>& set()
  5084.  
  5085.  
  5086.  
  5087.  
  5088. Description
  5089.  
  5090.  
  5091. Sets all bits.
  5092.  
  5093.  
  5094.  
  5095. Returns
  5096.  
  5097.  
  5098. A reference to *this.
  5099.  
  5100.  
  5101. 3.8 Functions reset
  5102.  
  5103.  
  5104.  
  5105.  
  5106. Synopsis
  5107.  
  5108.  
  5109. bits<N>& reset(size_t n)
  5110. bits<N>& reset()
  5111.  
  5112.  
  5113. Description
  5114.  
  5115.  
  5116. These functions reset one or more bits.
  5117.  
  5118.  
  5119. 3.8.1 Function bits<N>& reset(size_t n)
  5120.  
  5121.  
  5122.  
  5123.  
  5124. Description
  5125.  
  5126.  
  5127. Resets the nth bit.
  5128.  
  5129.  
  5130. Returns
  5131.  
  5132.  
  5133. A reference to *this.
  5134.  
  5135.  
  5136. Exceptions
  5137.  
  5138.  
  5139. Throws out_of_range if n is not in [0, N-1].
  5140.  
  5141.  
  5142. 3.8.2 Function bits<N>& reset()
  5143.  
  5144.  
  5145.  
  5146.  
  5147. Description
  5148.  
  5149.  
  5150. Resets all bits.
  5151.  
  5152.  
  5153. Returns
  5154.  
  5155.  
  5156.  
  5157. A reference to *this.
  5158.  
  5159.  
  5160. 3.9 Functions toggle
  5161.  
  5162.  
  5163.  
  5164.  
  5165. Synopsis
  5166.  
  5167.  
  5168. bits<N>& toggle(size_t n)
  5169. bits<N>& toggle()
  5170.  
  5171.  
  5172. Description
  5173.  
  5174.  
  5175. These functions toggle one or more bits.
  5176.  
  5177.  
  5178. 3.9.1 Function bits<N>& toggle(size_t n)
  5179.  
  5180.  
  5181.  
  5182.  
  5183. Description
  5184.  
  5185.  
  5186. Toggles the nth bit.
  5187.  
  5188.  
  5189. Returns
  5190.  
  5191.  
  5192. A reference to *this.
  5193.  
  5194.  
  5195. Exceptions
  5196.  
  5197.  
  5198. Throws out_of_range if n is not in [0,N-1].
  5199.  
  5200.  
  5201. 3.9.2 Function bits<N>& toggle()
  5202.  
  5203.  
  5204.  
  5205.  
  5206. Description
  5207.  
  5208.  
  5209. Toggles all bits.
  5210.  
  5211.  
  5212. Returns
  5213.  
  5214.  
  5215.  
  5216. A reference to *this.
  5217.  
  5218.  
  5219. 3.10 Function bits operator~() const
  5220.  
  5221.  
  5222.  
  5223.  
  5224. Description
  5225.  
  5226.  
  5227. Toggles all the bits of a copy of *this.
  5228.  
  5229.  
  5230. Returns
  5231.  
  5232.  
  5233. A toggled copy of *this.
  5234.  
  5235.  
  5236. 3.11 Function int test(size_t n) const
  5237.  
  5238.  
  5239.  
  5240.  
  5241. Description
  5242.  
  5243.  
  5244. Tests if bit n is set.
  5245.  
  5246.  
  5247. Returns
  5248.  
  5249.  
  5250. Non-zero if the bit is set, zero otherwise.
  5251.  
  5252.  
  5253. Exceptions
  5254.  
  5255.  
  5256. Throws out_of_range if n is not in [0,N-1].
  5257.  
  5258.  
  5259. 3.12 Function int any() const
  5260.  
  5261.  
  5262.  
  5263.  
  5264. Description
  5265.  
  5266.  
  5267. Tests if any bits at all are set.
  5268.  
  5269.  
  5270. Returns
  5271.  
  5272.  
  5273. 0 if all bits are 0, non-zero otherwise.
  5274.  
  5275.  
  5276.  
  5277. 3.13 Function int none() const
  5278.  
  5279.  
  5280.  
  5281.  
  5282. Description
  5283.  
  5284.  
  5285. Tests if no bits at all are set.
  5286.  
  5287.  
  5288. Returns
  5289.  
  5290.  
  5291. Non-zero if all bits are 0, 0 otherwise.
  5292.  
  5293.  
  5294. 3.14 Function bits<N>& operator&=(const bits<N>& b)
  5295.  
  5296.  
  5297.  
  5298.  
  5299. Description
  5300.  
  5301.  
  5302. Performs a destructive bitwise-AND of b into *this.
  5303.  
  5304.  
  5305. Returns
  5306.  
  5307.  
  5308. A reference to *this.
  5309.  
  5310.  
  5311. 3.15 Function bits<N>& operator/=(const bits<N>& b)
  5312.  
  5313.  
  5314.  
  5315.  
  5316. Description
  5317.  
  5318.  
  5319. Performs a destructive bitwise-OR of b into *this.
  5320.  
  5321.  
  5322. Returns
  5323.  
  5324.  
  5325. A reference to *this.
  5326.  
  5327.  
  5328. 3.16 Function bits<N>& operator^=(const bits<N>& b)
  5329.  
  5330.  
  5331.  
  5332.  
  5333. Description
  5334.  
  5335.  
  5336.  
  5337. Performs a destructive bitwise exclusive-OR of b into *this.
  5338.  
  5339.  
  5340. Returns
  5341.  
  5342.  
  5343. A reference to *this.
  5344.  
  5345.  
  5346. 3.17 Function bits<N>& operator>>=(size_t n)
  5347.  
  5348.  
  5349.  
  5350.  
  5351. Description
  5352.  
  5353.  
  5354. Shifts *this right destructively (i.e., in place) by n bit positions. Resets
  5355. all bits if n > N. To shift "right" by n means that bit 0 receives the value
  5356. of bit n, bit 1 receives bit (n+1), etc.
  5357.  
  5358.  
  5359. Returns
  5360.  
  5361.  
  5362. A reference to *this.
  5363.  
  5364.  
  5365. 3.18 Function bits<N>& operator<<=(size_t n)
  5366.  
  5367.  
  5368.  
  5369.  
  5370. Description
  5371.  
  5372.  
  5373. Shifts *this left destructively by n bit positions. Resets all bits if n > N.
  5374. To shift "left" by n means that bit n receives the value of bit 0, bit (n+1)
  5375. receives bit 1, etc.
  5376.  
  5377.  
  5378. Returns
  5379.  
  5380.  
  5381. A reference to *this.
  5382.  
  5383.  
  5384. 3.19 Function bits<N> operator>>(size_t n) const
  5385.  
  5386.  
  5387.  
  5388.  
  5389. Description
  5390.  
  5391.  
  5392. A non-destructive version of operator>>=().
  5393.  
  5394.  
  5395. Returns
  5396.  
  5397.  
  5398.  
  5399. The results of the shift in a temporary bits object.
  5400.  
  5401.  
  5402. 3.20 Function bits<N> operator<<(size_t n) const
  5403.  
  5404.  
  5405.  
  5406.  
  5407. Description
  5408.  
  5409.  
  5410. A non-destructive version of operator<<=().
  5411.  
  5412.  
  5413. Returns
  5414.  
  5415.  
  5416. The results of the shift in a temporary bits object.
  5417.  
  5418.  
  5419. 3.21 Function size_t count( ) const
  5420.  
  5421.  
  5422.  
  5423.  
  5424. Description
  5425.  
  5426.  
  5427. Counts the number of bits that are set.
  5428.  
  5429.  
  5430. Returns
  5431.  
  5432.  
  5433. The number of 1 bits in *this.
  5434.  
  5435.  
  5436. 3.22 Function size_t length( ) const
  5437.  
  5438.  
  5439.  
  5440.  
  5441. Description
  5442.  
  5443.  
  5444. Returns the fixed-size length of the object.
  5445.  
  5446.  
  5447. Returns
  5448.  
  5449.  
  5450. N.
  5451.  
  5452.  
  5453. 4.0 Global Functions
  5454.  
  5455.  
  5456.  
  5457.  
  5458.  
  5459. 4.1 Function ostream& operator<<(ostream& os, const bits<N>& b)
  5460.  
  5461.  
  5462.  
  5463.  
  5464. Description
  5465.  
  5466.  
  5467. Sends a sequence of N 1s and 0s corresponding to the bit pattern of *this to
  5468. the stream os,
  5469.  
  5470.  
  5471. Returns
  5472.  
  5473.  
  5474. A reference to the stream os.
  5475.  
  5476.  
  5477. 4.2 Function istream& operator>>(istream& is, bits<N>& b)
  5478.  
  5479.  
  5480.  
  5481.  
  5482. Description
  5483.  
  5484.  
  5485. Reads a sequence of up to N 1s and 0s from the stream is, after skipping
  5486. whitespace. The first non-bit character thereafter terminates the read and
  5487. remains in the stream. The corresponding bit pattern is reproduced in b.
  5488. Treats the last 1 or 0 read from the stream as bit 0 of b.
  5489.  
  5490.  
  5491. Returns
  5492.  
  5493.  
  5494. A reference to the stream is.
  5495.  
  5496.  
  5497. 4.3 Function bits<N>operator& (const bits<N>& b1, const bits<N>& b2)
  5498.  
  5499.  
  5500.  
  5501.  
  5502. Description
  5503.  
  5504.  
  5505. Performs a bitwise-AND of b1 and b.
  5506.  
  5507.  
  5508. Returns
  5509.  
  5510.  
  5511. The results of the bitwise-AND in a temporary bits object.
  5512.  
  5513.  
  5514. 4.4 Function bits<N>operator / (const bits<N>& b1, const bits<N>& b2)
  5515.  
  5516.  
  5517.  
  5518.  
  5519. Description
  5520.  
  5521.  
  5522.  
  5523. Performs a bitwise-OR of b1 and b.
  5524.  
  5525.  
  5526. Returns
  5527.  
  5528.  
  5529. The results of the bitwise-OR in a temporary bits object.
  5530.  
  5531.  
  5532. 4.5 Function bits <N> operator^ (const bits<N>& b1, const bits<N>& b2)
  5533.  
  5534.  
  5535.  
  5536.  
  5537. Description
  5538.  
  5539.  
  5540. Performs a bitwise exclusive-OR of b1 and b.
  5541.  
  5542.  
  5543. Returns
  5544.  
  5545.  
  5546. The results of the exclusive-OR in a temporary bits object.
  5547.  
  5548.  
  5549. Design Notes
  5550.  
  5551.  
  5552. Having an expression instead of a type as a template parameter has the
  5553. following effects:
  5554. A bits-object can be stack-based, since its size is known at compile time.
  5555. This means less run-time overhead and therefore better performance than
  5556. bitstring objects.
  5557. Objects of different sizes (i.e., with a different number of bits) are
  5558. different types, and therefore can't be combined in operations.
  5559. No global functions taking bits arguments are allowed under the current
  5560. definition of the language unless you define them inline in the class
  5561. definition. The standards committee is working to fix this. See the sidebar,
  5562. "Templates in a Nutshell," for more detail.
  5563. Since a bits object is an extension of unsigned integers as far as bitwise
  5564. operations are concerned, a collection of bits behaves like a number, in that
  5565. bit 0 is the right-most bit. To be consistent with C bitwise operations, the
  5566. statements
  5567. bits<8> b;
  5568. b = 5;
  5569. cout << b << endl;
  5570. give the result
  5571. 00000101
  5572. that is, the bits of 5 are ORed with b (via the constructor bits(unsigned
  5573. long)).
  5574. As you can see in Listing 7, I've taken some liberties in the implementation
  5575. of this class by changing the type of the template parameter to a size_t. The
  5576. reason for originally making the number of bits an unsigned long was to
  5577. guarantee a minimum size across platforms (the ANSI C standard states that an
  5578. unsigned long must be at least 32 bits wide). However, this would require that
  5579. count and length return an unsigned long. As proof that this is unnatural, I
  5580. offer the fact that I forgot to do so (the functions return a size_t because
  5581. it "feels right") and no one on the committee noticed. (Actually, Bill Plauger
  5582. finally noticed while he was editing the standard library documents, but that
  5583. was four months after the bits class became official.) Furthermore, the
  5584. corresponding functions in the string and bitstring classes also return a
  5585. size_t. (They have no choice.) To be consistent with these classes, therefore,
  5586. and because it just makes sense, I shall propose to the committee that we
  5587. approve the obvious and make the template parameter a size_t.
  5588. I'm also thinking of changing the name from bits to bitset. This would allow
  5589. you to refer to an object as a "bitset" instead of always having to say a
  5590. "bits object." (Who would ever call it a "bits"?) And one could argue that the
  5591. function to_ushort( ) is superfluous, since it is equivalent to (unsigned
  5592. short) to_ulong( ).
  5593.  
  5594.  
  5595. Implementation Notes
  5596.  
  5597.  
  5598. The Code Capsule "Bit Handling in C" (CUJ, November, 1993) provides a thorough
  5599. explanation of the internals of using an integral array to store and access
  5600. individual bits, so I won't repeat it here. The techniques found therein serve
  5601. both the bitstring and bits classes. The implementation of the string class is
  5602. in last month's installment ("Bit Handling in C++, Part 1," CUJ, December
  5603. 1993). Listing 8 has a test program that exercises most of the member
  5604. functions of the bits class.
  5605.  
  5606.  
  5607. Sets of Integers
  5608.  
  5609.  
  5610. For those of you who miss some of the high-level features of Pascal and
  5611. Modula-2, the bits class gives you sets of integers almost for free. Just
  5612. define a bits object of a size appropriate for your application and do the
  5613. following:
  5614. For the set operation: Do this:
  5615. insert x into s s. set(x)
  5616. remove x from s s. reset(x)
  5617. x member of s? s. test(x)
  5618. complement of s s. toggle() or ~s
  5619.  
  5620. s1 + s2 (union) s1 / s2
  5621. s1 * s2 (intersection) s2 & s2
  5622. s1 - s2 (difference) see below
  5623. s1 <= s2 (subset) see below
  5624. s1 >= s2 (superset) see below
  5625. If this still seems too "low-level," it is a trivial matter to define a
  5626. set-like interface to the bits class. Listing 9 defines a class template
  5627. called Intset that has all the basic set operations along with an ostream
  5628. inserter. The only operations that take any thought at all (but only very
  5629. little) are set difference and subset. To remove from s1 the elements of s2,
  5630. just reset in s1 the bits that are set in s2:
  5631. s1.bitset &= ~s2.bitset;
  5632. // see Intset<N>::operator-
  5633. If s1 is a subset of s2, then s1 is nothing more nor less than the
  5634. intersection of the two sets:
  5635. s1 == s1 & s2
  5636. // true iff s1 <= s2
  5637. The test program in Listing 10 shows how to use the Intset class.
  5638.  
  5639.  
  5640. Conclusion
  5641.  
  5642.  
  5643. The acceptance of these two bit handling classes by the C++ standards
  5644. committee shows that the needs of the system programmer have not been
  5645. forgotten. Much of the hullabaloo over object-oriented technology emphasizes
  5646. inheritance hierarchies of polymorphic, high-level abstract data types. These
  5647. concepts are best left to specialized libraries and applications while the
  5648. standard rightly focuses on commonly needed, low-level abstractions. If you
  5649. have any comments on these classes, please contact me at the email address in
  5650. the by-line at the bottom of the first page of this article.
  5651. Templates In A Nutshell
  5652. A template is a parameterized layout for defining a function or class. The
  5653. parameters are usually types, but class templates can also have value
  5654. parameters. Template definitions allow you to specify the logic of a function
  5655. or class once and then have the compiler create specific functions or classes
  5656. for different types as you need them.
  5657.  
  5658.  
  5659. Function Templates
  5660.  
  5661.  
  5662. Consider the swap function
  5663. void swap(int& x, int& y)
  5664. {
  5665. int temp = x;
  5666. x = y;
  5667. temp = y;
  5668. }
  5669. This works only for integer arguments. You need a different version of swap
  5670. for each data type for which you want to swap elements. If you inspect the
  5671. version for doubles:
  5672. void swap(double& x, double& y)
  5673. {
  5674. double temp = x;
  5675. x = y;
  5676. temp = y;
  5677. }
  5678. you'll notice that the only change was to substitute double for int in the
  5679. text. This suggests a macro solution, with the data type as a parameter:
  5680. // genswap.h
  5681. #define genswap(T) void swap(T& x, T& y) \
  5682. { \
  5683. T temp = x; \
  5684. x = y; \
  5685. y = temp; \
  5686. }
  5687. To generate different versions of swap, call genswap as needed:
  5688. #include <iostream.h>
  5689. #include "genswap.h"
  5690.  
  5691. genswap(int) // Awkward syntax,
  5692. genswap(double) // I'll admit.
  5693.  
  5694. main()
  5695. {
  5696. int i = 1, j = 2;
  5697. double x = 1.1, y = 2.2;
  5698. swap(i,j);
  5699.  
  5700. swap(x,y);
  5701. cout << i << ',' << j << endl; // 2,1
  5702. cout << x << ',' << y << endl; // 2.2,1.1
  5703. return 0;
  5704. }
  5705. This has the advantage of allowing you to specify the function logic only
  5706. once.
  5707. A function template is much the same as the genswap macro, except that you
  5708. don't have to explicitly generate the functions you need. After seeing the
  5709. template definition
  5710. template<class T>
  5711. void swap(T& x, T& y)
  5712. {
  5713. T temp = x;
  5714. x = y;
  5715. y = temp;
  5716. }
  5717. the compiler automatically generates versions as needed when it finds a call
  5718. to swap. (See Listing 1.) You can use any type, including built-ins, for the
  5719. template argument.
  5720.  
  5721.  
  5722. Class Templates
  5723.  
  5724.  
  5725. You can also parameterize class definitions. A good candidate is a stack,
  5726. since the logic of stack operations is the same no matter what type the stack
  5727. elements are. Listing 2 and Listing 3 have the definition of an integer stack
  5728. class. To templatize this class, precede the class definition with the line
  5729. template<class T>
  5730. as before, and change all occurrences of int that refer to the type of
  5731. elements on the stack to T (see Listing 4). You instantiate a specific stack
  5732. class like this:
  5733. Stack<int> s1(5);
  5734. The type of s1 is Stack<int> ("stack of int"). The token Stack cannot appear
  5735. unqualified outside of the class template definition. See Listing 5 for an
  5736. example of using Stack template classes. (Point of Terminology: A "class
  5737. template" is the original template definition. A "template class" is a
  5738. particular class instantiated from the class template, such as Stack<int>.)
  5739. Note that there is no separate stack2.cpp file. My compilers (Borland 3.1 and
  5740. WATCOM 9.5) require the entire class implementation to be visible during
  5741. compilation, so everything is in an include file.
  5742. A class template can also have value parameters. The bits class in this
  5743. article is a good example:
  5744. template<size_t N>
  5745. class bits
  5746. {
  5747. //...
  5748. };
  5749. The value for N must be a constant expression when instantiated:
  5750. bits<16>b1; // ok
  5751. const size_t n = 32;
  5752. bits<n> b2; // ok
  5753. size_t m = 64;
  5754. bits<m> b3; // nope - m not const
  5755. Since the specific values of N in a program are known at compile time, the
  5756. array inside a bits<N> object can be placed on the stack, thus avoiding the
  5757. need for dynamic memory management.
  5758. A disadvantage of value parameters occurs with friend functions. Consider the
  5759. friend function operator& defined in the bits class. To define this outside of
  5760. the class definition itself, you would have to write:
  5761. template<size_t N>
  5762. bits<N> operator&(const bits<N>& b1, const bits<N>& b2)
  5763. {
  5764. //...
  5765. }
  5766. Since this is not a member function, the compiler recognizes it as a function
  5767. template definition. Under the current definition of the language, global
  5768. function templates can only have type arguments (e.g., class T), because a
  5769. compiler uses overloading rules to resolve them. That's why I had to fully
  5770. define all friends inside of the bits<N> class template definition. The joint
  5771. C++ standards committee voted in November to allow out-of-line definitions of
  5772. friend functions for class templates.
  5773.  
  5774. Listing 1 A function template for swapping two objects of the same type
  5775. // swap.cpp
  5776. #include <iostream.h>
  5777.  
  5778. template<class T>
  5779. void swap(T& x, T& y)
  5780. {
  5781. T temp = x;
  5782. x = y;
  5783. y = temp;
  5784. }
  5785.  
  5786.  
  5787. main()
  5788. {
  5789. int a = 1, b = 2;
  5790. double c = 1.1, d = 2;
  5791. char *s = "hello", *t = "there";
  5792.  
  5793. swap(a,b);
  5794. cout << "a = " << a << ", b = " << b << '\n';
  5795.  
  5796. swap(c,d);
  5797. cout<< "c = " << c << ", d = " << d << '\n';
  5798.  
  5799. swap(s,t);
  5800. cout<< "s = " << s << ", t = " << t << '\n';
  5801.  
  5802. return 0;
  5803. }
  5804.  
  5805. /* Output;
  5806. a = 2, b = 1
  5807. c = 2.2, d = 1.1
  5808. s = there, t = hello
  5809.  
  5810. // End of File
  5811.  
  5812.  
  5813. Listing 2 A class for a stack of integers
  5814. // stack1.h: A C++ integer stack class
  5815.  
  5816. #include <stddef.h>
  5817.  
  5818. class Stack
  5819. {
  5820.  
  5821. size_t size;
  5822. int *data;
  5823. int ptr;
  5824.  
  5825. public:
  5826. Stack(size_t);
  5827. ~Stack();
  5828. void push(int);
  5829. int pop();
  5830. int empty() const;
  5831. int full() const;
  5832. };
  5833.  
  5834. inline Stack::Stack(size_t siz)
  5835. {
  5836. data = new int[size = siz];
  5837. ptr = 0;
  5838. }
  5839.  
  5840. inline Stack::~Stack()
  5841. {
  5842. delete [] data;
  5843. }
  5844.  
  5845. inline int Stack::empty() const
  5846.  
  5847. {
  5848. return ptr == 0;
  5849. }
  5850.  
  5851. inline int Stack::full() const
  5852. {
  5853. return ptr == size;
  5854. }
  5855.  
  5856. // End of File
  5857.  
  5858.  
  5859. Listing 3 Out-of-line functions for the stack class
  5860. // stack1.cpp
  5861. #include "stack1.h"
  5862.  
  5863. void Stack::push(int x)
  5864. {
  5865. if (ptr < size)
  5866. data[ptr++] = x;
  5867. }
  5868.  
  5869. int Stack::pop()
  5870. {
  5871. if (ptr > 0)
  5872. --ptr;
  5873. return data[ptr];
  5874. }
  5875.  
  5876. // End of File
  5877.  
  5878.  
  5879. Listing 4 A class template for homogeneous stacks 
  5880. // stack.h: A C++ stack class template
  5881.  
  5882. #include <stddef.h>
  5883.  
  5884. template<class T>
  5885. class Stack
  5886. {
  5887.  
  5888. size_t size;
  5889. T *data;
  5890. int ptr;
  5891.  
  5892. public:
  5893. Stack(size_t);
  5894. ~Stack();
  5895. void push(const T&);
  5896. T pop();
  5897. int empty() const;
  5898. int full() const;
  5899. };
  5900.  
  5901. template<class T>
  5902. inline Stack<T>::Stack(size_t siz)
  5903. {
  5904. data = new T[size = siz];
  5905. ptr = 0;
  5906.  
  5907. }
  5908.  
  5909. template<class T>
  5910. inline Stack<T>::~Stack()
  5911. {
  5912.  
  5913. delete [] data;
  5914. }
  5915.  
  5916. template<class T>
  5917. void Stack<T>::push(const T& x)
  5918. {
  5919. if (ptr < size)
  5920. data[ptr++] = x;
  5921. }
  5922.  
  5923. template<class T>
  5924. T Stack<T>::pop()
  5925. {
  5926. if (ptr > 0)
  5927. --ptr;
  5928. return data[ptr];
  5929. }
  5930.  
  5931. template<class T>
  5932. inline int Stack<T>::empty() const
  5933. {
  5934. return ptr == 0;
  5935. }
  5936.  
  5937. template<class T>
  5938. inline int Stack<T>::full() const
  5939. {
  5940. return ptr == size;
  5941. }
  5942.  
  5943. // End of File
  5944.  
  5945.  
  5946. Listing 5 Illustrates the stack template class
  5947. // tstack2.h
  5948. #include <iostream.h>
  5949. #include "stack2.h"
  5950.  
  5951. main()
  5952. {
  5953. Stack<int> s1(5), s2(5);
  5954.  
  5955. // Push odds onto s1, evens onto s2:
  5956. for (int i = 1; i < 10; i += 2)
  5957. {
  5958. s1.push(i);
  5959. s2.push(i+1);
  5960. }
  5961.  
  5962. // Retrieve and print in LIFO order:
  5963. cout << "Stack 1:\n";
  5964. while (!s1.empty())
  5965. cout << s1.pop() << endl;
  5966.  
  5967.  
  5968. cout << "Stack 2:\n";
  5969. while (!s2.empty())
  5970. cout << s2.pop() << endl;
  5971.  
  5972. return 0;
  5973. }
  5974.  
  5975. /* Output:
  5976. Stack 1:
  5977. 9
  5978. 7
  5979. 5
  5980. 3
  5981. 1
  5982. Stack 2:
  5983. 10
  5984. 8
  5985. 6
  5986. 4
  5987. 2
  5988. */
  5989.  
  5990. /* End of File */
  5991.  
  5992.  
  5993. Listing 6 The bits class template interface
  5994. template<unsigned long N>
  5995. class bits
  5996. {
  5997. // Friends:
  5998. // Global I/O funtions
  5999. friend ostream& operator<<(ostream&, const bits<N>&);
  6000. friend istream& operator>>(istream&, bits<N>&);
  6001.  
  6002. // Global bitwise operators
  6003. friend bits<N> operator&(const bits<N>&, const bits<N>&);
  6004. friend bits<N> operator(const bits<N>&, const bits<N>&);
  6005. friend bits<N> operator^(const bits<N>&, const bits<N>&);
  6006.  
  6007. public:
  6008. // Constructors
  6009. bits();
  6010. bits(unsigned long n);
  6011. bits(const bits<N>& b);
  6012. bits(const string& s);
  6013.  
  6014. // Conversions
  6015. unsigned short to_ushort() const;
  6016. unsigned long to_ulong() const;
  6017. string to_string() const;
  6018.  
  6019. // Assignment
  6020. bits<N>& operator=(const bits<N>& rhs);
  6021.  
  6022. // Equality
  6023. int operator==(const bits<N>& rhs) const;
  6024. int operator!=(const bits<N>& rhs) const;
  6025.  
  6026.  
  6027. // Basic bit operations
  6028. bits<N>& set(size_t pos, int val = 1);
  6029. bits<N>& set();
  6030. bits<N>& reset(size_t pos);
  6031. bits<N>& reset();
  6032. bits<N>& toggle(size_t pos);
  6033. bits<N>& toggle();
  6034. bits<N> operator~() const;
  6035. int test(size_t n) const;
  6036. int any() const;
  6037. int none() const;
  6038.  
  6039. // Bit-wise operators
  6040. bits<N>& operator&=(const bits<N>& rhs);
  6041. bits<N>& operator=(const bits<N>& rhs);
  6042. bits<N>& operator^=(const bits<N>& rhs);
  6043.  
  6044. // Shift operators
  6045. bits<N>& operator<<=(size_t n);
  6046. bits<N>& operator>>=(size_t n);
  6047. bits<N> operator<<(size_t n) const;
  6048. bits<N> operator>>(size_t n) const;
  6049.  
  6050. size_t count() const;
  6051. size_t length() const;
  6052. };
  6053.  
  6054. // End of File
  6055.  
  6056.  
  6057. Listing 7 An implementation of the bits class template
  6058. // bits.h
  6059.  
  6060. #include <iostream. h>
  6061. #include <stddef. h>
  6062. #include <limits.h>
  6063. #include <assert.h>
  6064. #include "string.hpp"
  6065.  
  6066. template<size_t N>
  6067. class bits
  6068. {
  6069. // Global I/O funtions
  6070. friend ostream& operator<<(ostream& os, const bits<N>& rhs)
  6071. {os << rhs.to_string(); return os;}
  6072. friend istream& operator>>(istream& is, bits<N>& rhs)
  6073. {rhs.read(is); return is;}
  6074.  
  6075. // Global bitwise operators
  6076. friend bits<N>operator&(const bits<N>& b1,const bits<N>& b2)
  6077. {bits<N> r(b1); return r &= b2;}
  6078. friend bits<N>operator(const bits<N>&b1,const bits<N>& b2)
  6079. {bits<N> r(b1); return r = b2;}
  6080. friend bits<N> operator^(const bits<N>& b1,const bits<N>& b2)
  6081. {bits<N> r(b1); return r ^= b2;}
  6082.  
  6083. public:
  6084. // Constructors
  6085. bits();
  6086.  
  6087. bits(unsigned long n);
  6088. bits(const bits<N>& b);
  6089. bits(const string& s);
  6090.  
  6091. // Conversions
  6092. unsigned short to_ushort() const;
  6093. unsigned long to_ulong() const;
  6094. string to_string() const;
  6095.  
  6096. // Assignment
  6097. bits<N>& operator=(const bits<N>& rhs);
  6098.  
  6099. // Equality
  6100. int operator==(const bits<N>& rhs) const;
  6101. int operator!=(const bits<N>& rhs) const;
  6102.  
  6103. // Basic bit operations
  6104. bits<N>& set(size_t pos, int val = 1);
  6105. bits<N>& set();
  6106. bits<N>& reset(size_t pos);
  6107. bits<N>& reset();
  6108. bits<N>& toggle(size_t pos);
  6109. bits<N>& toggle();
  6110. bits<N> operator~() const;
  6111. int test(size_t n) const;
  6112. int any() const;
  6113. int none() const;
  6114.  
  6115. // Bit-wise operators
  6116. bits<N>& operator&=(const bits<N>& rhs);
  6117. bits<N>& operator=(const bits<N>& rhs);
  6118. bits<N>& operator^=(const bits<N>& rhs);
  6119.  
  6120. // Shift operators
  6121. bits<N>& operator<<=(size_t n);
  6122. bits<N>& operator>>=(size_t n);
  6123. bits<N> operator<<(size_t n) const;
  6124. bits<N> operator>>(size_t n) const;
  6125.  
  6126. size_t count() const;
  6127. size_t length() const;
  6128.  
  6129. private:
  6130. typedef unsigned int Block;
  6131. enum {BLKSIZ = CHAR_BIT * sizeof (Block)};
  6132. enum {nblks_ = (N+BLKSIZ-1) / BLKSIZ};
  6133.  
  6134. Block bits_[nblks_];
  6135.  
  6136. static size_t word(size_t pos)
  6137. {return nblks_ - 1 - pos/BLKSIZ;}
  6138. static size_t offset(size_t pos)
  6139. {return pos % BLKSIZ;}
  6140. static Block mask1(size_t pos)
  6141. {return Block(1) << offset(pos);}
  6142. static Block mask0(size_t pos)
  6143. {return ~(Block(1) << offset(pos));}
  6144.  
  6145. void cleanup();
  6146.  
  6147. void set_(size_t pos);
  6148. int set_(size_t pos, int val);
  6149. void reset_(size_t pos);
  6150. int test_(size_t pos) const;
  6151. void from_string(const string& s);
  6152. void read(istream& is);
  6153. int any(size_t start_pos) const;
  6154. unsigned long to(size_t) const;
  6155. };
  6156.  
  6157. template<size_t N>
  6158. inline bits<N>::bits()
  6159. {
  6160. reset();
  6161. }
  6162.  
  6163. template<size_t N>
  6164. bits<N>::bits(const string& s)
  6165. {
  6166. // Validate that s has only 0's and 1's
  6167. for (int i = 0; i < s.length(); ++i)
  6168. {
  6169. char c = s.get_at(i);
  6170. if (c != '0' && c != '1')
  6171. break;
  6172. }
  6173. assert(i == s.length());
  6174.  
  6175. from_string(s);
  6176. }
  6177.  
  6178. template<size_t N>
  6179. inline bits<N>::bits(const bits<N>& b)
  6180. {
  6181. memcpy(bits_,b.bits_,nblks_*sizeof(bits_[0]));
  6182. }
  6183.  
  6184. template<size_t N>
  6185. bits<N>::bits(unsigned long n)
  6186. {
  6187. // Don't drop any bits
  6188. if (N < CHAR_BIT * sizeof(unsigned long))
  6189. assert((n >> N) == 0);
  6190.  
  6191. reset();
  6192.  
  6193. size_t nblks = sizeof (unsigned long) / sizeof (Block);
  6194. if (nblks > 1)
  6195. for (int i = 0; i < nblks; ++i)
  6196. {
  6197. bits_[nblks - 1 - i] = Block(n);
  6198. n >>= BLKSIZ;
  6199. }
  6200. else
  6201. bits_[nblks_ - 1] = Block(n);
  6202. }
  6203.  
  6204. template<size_t N>
  6205. unsigned short bits<N>::to_ushort() const
  6206.  
  6207. {
  6208. size_t limit = sizeof(unsigned short) * CHAR_BIT;
  6209. assert(!(length() > limit && any(limit)));
  6210. size_t nblks = sizeof(unsigned short) / sizeof(Block);
  6211. return (unsigned short) to(nblks);
  6212. }
  6213. template<size_t N>
  6214. unsigned long bits<N>::to_ulong() const
  6215. {
  6216. size_t limit= sizeof(unsigned long) * CHAR_BIT;
  6217. assert(!(length() > limit && any(limit)));
  6218. size_t nblks = sizeof(unsigned long) / sizeof(Block);
  6219. return to(nblks);
  6220. }
  6221.  
  6222. template<size_t N>
  6223. string bits<N>::to_string() const
  6224. {
  6225. char *s = new char[N+1];
  6226. for (int i = 0; i < N;++i)
  6227. s[i] = '0' + test_(N-1-i);
  6228. s[N] = '\0';
  6229. string s2(s);
  6230. delete [] s;
  6231. return s2;
  6232. }
  6233.  
  6234. template<size_t N>
  6235. bits<N>& bits<N>::operator=(const bits<N>& b)
  6236. {
  6237. if (this != &b)
  6238. memcpy(bits_,b.bits_, nblks_* sizeof(bits_[0]));
  6239. return *this;
  6240. }
  6241.  
  6242. template<size_t N>
  6243. inline int bits<N>::operator==(const bits<N>& b) const
  6244. {
  6245. return !memcmp(bits_,b.bits_,nblks_ * sizeof(bits_[0]));
  6246. }
  6247.  
  6248. template<size_t N>
  6249. inline int bits<N>::operator!=(const bits<N>& b) const
  6250. {
  6251. return !operator==(b);
  6252. }
  6253.  
  6254. template<size_t N>
  6255. inline bits<N>& bits<N>::set(size_t pos, int val)
  6256. {
  6257. assert(pos < N);
  6258. (void) set_(pos,val);
  6259. return *this;
  6260. }
  6261.  
  6262. template<size_t N>
  6263. inline bits<N>& bits<N>::set()
  6264. {
  6265. memset(bits_,~0u,nblks_ * sizeof bits_[0]);
  6266.  
  6267. cleanup();
  6268. return *this;
  6269. }
  6270.  
  6271. template<size_t N>
  6272. inline bits<N>& bits<N>::reset(size_t pos)
  6273. {
  6274. assert(pos < N);
  6275. reset_(pos);
  6276. return *this;
  6277. }
  6278.  
  6279. template<size_t N>
  6280. inline bits<N>& bits<N>::reset()
  6281. {
  6282. memset(bits_,0,nblks_ * sizeof bits_[0]);
  6283. return *this;
  6284. }
  6285.  
  6286. template<size_t N>
  6287. inline bits<N>& bits<N>::toggle(size_t pos)
  6288. {
  6289. assert(pos < N);
  6290. bits_[word(pos)] ^= mask1(pos);
  6291. return *this;
  6292. }
  6293.  
  6294. template<size_t N>
  6295. bits<N>& bits<N>::toggle()
  6296. {
  6297. size_t nw = nblks_;
  6298. while (nw--)
  6299. bits_[nw] = ~bits_[nw];
  6300. cleanup();
  6301. return *this;
  6302. }
  6303.  
  6304. template<size_t N>
  6305. inline bits<N> bits<N>::operator~() const
  6306. {
  6307. bits<N> b(*this);
  6308. b.toggle();
  6309. return b;
  6310. }
  6311.  
  6312. template<size_t N>
  6313. inline int bits<N>::test(size_t pos) const
  6314. {
  6315. assert(pos < N);
  6316. return test_(pos);
  6317. }
  6318.  
  6319. template<size_t N>
  6320. int bits<N>::any() const
  6321. {
  6322. for (int i = 0; i < nblks_; ++i)
  6323. if (bits_[i])
  6324. return 1;
  6325. return 0;
  6326.  
  6327. }
  6328.  
  6329. template<size_t N>
  6330. inline int bits<N>::none() const
  6331. {
  6332. return !any();
  6333. }
  6334.  
  6335. template<size_t N>
  6336. bits<N>& bits<N>::operator&=(const bits<N>& rhs)
  6337. {
  6338. for (int i = 0; i < nblks_; ++i)
  6339. bits_[i] &= rhs.bits_[i];
  6340. return *this;
  6341. }
  6342.  
  6343. template<size_t N>
  6344. bits<N>& bits<N>::operator=(const bits<N>& rhs)
  6345. {
  6346. for (int i = 0; i < nblks_; ++i)
  6347. bits_[i] = rhs.bits_[i];
  6348. return *this;
  6349. }
  6350.  
  6351. template<size_t N>
  6352. bits<N>& bits<N>::operator^=(const bits<N>& rhs)
  6353. {
  6354. for (int i= 0; i < nblks ; ++i)
  6355. bits_[i] ^= rhs.bits_[i];
  6356. return *this;
  6357. }
  6358.  
  6359. template<size_t N>
  6360. bits<N>& bits<N>::operator>>=(size_t n)
  6361. {
  6362. if (n > N)
  6363. n = N;
  6364. for (int i = 0; i < N-n;++i)
  6365. (void) set_(i,test_(i+n));
  6366. for (i = N-n; i < N; ++i)
  6367. reset_(i);
  6368. return *this;
  6369. }
  6370.  
  6371. template<size_t N>
  6372. bits<N>& bits<N>::operator<<=(size_t n)
  6373. {
  6374. if (n > N)
  6375. n = N;
  6376. for (int i = N-1; i >= n; --i)
  6377. (void) set_(i,test(i-n));
  6378. for (i = 0; i < n; ++i)
  6379. reset_(i);
  6380. return *this;
  6381. }
  6382.  
  6383. template<size_t N>
  6384. inline bits<N> bits<N>::operator>>(size_t n) const
  6385. {
  6386.  
  6387. bits r(*this);
  6388. return r >>= n;
  6389. }
  6390.  
  6391. template<size_t N>
  6392. inline bits<N> bits<N>::operator<<(size_t n) const
  6393. {
  6394. bits r(*this);
  6395. return r <<= n;
  6396. }
  6397.  
  6398. template<size_t N>
  6399. size_t bits<N>::count() const
  6400. {
  6401. size_t sum = 0;
  6402. for (int i = 0; i < N; ++i)
  6403. if (test_(i))
  6404. ++sum;
  6405. return sum;
  6406. }
  6407.  
  6408. template<size_t N>
  6409. inline size_t bits<N>::length() const
  6410. {
  6411. return N;
  6412. }
  6413.  
  6414. // Private functions
  6415. template<size_t N>
  6416. inline void bits<N>::set_(size_t pos)
  6417. {
  6418. bits_[word(pos)] = mask1(pos);
  6419. }
  6420.  
  6421. template<size_t N>
  6422. int bits<N>::set_(size_t pos, int val)
  6423. {
  6424. if (val)
  6425. set_(pos);
  6426. else
  6427. reset_(pos);
  6428. return !!val;
  6429. }
  6430.  
  6431. template<size_t N>
  6432. inline void bits<N>::reset_(size_t pos)
  6433. {
  6434. bits_[word(pos)] &= mask0(pos);
  6435. }
  6436.  
  6437. template<size_t N>
  6438. inline int bits<N>::test_(size_t pos) const
  6439. {
  6440. return !!(bits_[word(pos)] & mask1(pos));
  6441. }
  6442.  
  6443. template<size_t N>
  6444. inline void bits<N>::cleanup()
  6445. {
  6446.  
  6447. // Make sure unused bits don't get set
  6448. bits_[0] &= (~Block(0) >> (nblks_ * BLKSIZ - N));
  6449. }
  6450.  
  6451. template<size_t N>
  6452. void bits<N>::from_string(const string& s)
  6453. {
  6454. // Assumes s contains only 0's and 1's
  6455. size_t slen = s.length();
  6456. reset();
  6457. for (int i = slen-1; i >= 0; --i)
  6458. if (s.get_at(i) == '1')set_(slen-i-1);
  6459. }
  6460.  
  6461. template<size_t N>
  6462. void bits<N>::read(istream& is)
  6463. {
  6464. char *buf = new char[N];
  6465. char c;
  6466.  
  6467. is >> ws;
  6468. for (int i = 0; i < N; ++i)
  6469. {
  6470. is.get(c);
  6471. if (c == '0' c == '1')
  6472. buf[i] = c;
  6473. else
  6474. {
  6475. is.putback(c);
  6476. buf[i] = '\0';
  6477. break;
  6478. }
  6479. }
  6480.  
  6481. if (i==0)
  6482. is.clear(ios::failbit);
  6483. else
  6484. from_string(string(buf));
  6485. delete buf;
  6486. }
  6487.  
  6488. template<size_t N>
  6489. int bits<N>::any(size_t start) const
  6490. {
  6491. // See if any bit past start (inclusive) is set
  6492. for (int i = start; i < N; ++i)
  6493. if (test_(i))
  6494. return 1;
  6495. return 0;
  6496. }
  6497.  
  6498. template<size_t N>
  6499. unsigned long bits<N>::to(size_t nblks) const
  6500. {
  6501. if (nblks > 1)
  6502. {
  6503. int i;
  6504. unsigned long n = bits_[nblks_ - nblks];
  6505.  
  6506.  
  6507. /* Collect low-order sub-blocks into an unsigned */
  6508. if (nblks > nblks_)
  6509. nblks = nblks_;
  6510. while (--nblks)
  6511. n = (n << BLKSIZ) bits_[nblks_ - nblks];
  6512. return n;
  6513. }
  6514. else
  6515. return (unsigned long) bits_[nblks_ - 1];
  6516.  
  6517. }
  6518.  
  6519. /* End of File */
  6520.  
  6521.  
  6522. Listing 8 Tests the bits class
  6523. // tbits.cpp
  6524. #include <i0stream.h>
  6525. #include <i0manip.h>
  6526. #include <stddef.h>
  6527. #include <limits.h>
  6528. #include "bits.h"
  6529.  
  6530. main()
  6531. {
  6532. const size_t SIZE = CHAR_BIT * sizeof(unsigned long);
  6533. unsigned long n = 0x12345678;
  6534. bits<SIZE> x(n), y(string("10110")), z(x);
  6535.  
  6536. cout << "Initial x: "<< x << endl;
  6537. cout << "Initial y: "<< y << endl;
  6538. cout << "Initial z: "<< z << endl;
  6539. cout << "Enter new z: "/;
  6540. cin >> z;
  6541. cout << "New z: "<< z << endl;
  6542. cout << "z == "<< z.to_ulong() << endl;
  6543. cout << "y ==" << y.to_ushort() << endl;
  6544. cout << "x ==" << x.to_ulong() << endl;
  6545.  
  6546. cout << "x: "<< x <<" (" << x.count()
  6547. <<" bits set)" << endl;
  6548. cout << "x == 0x12345678L? "<< (x == 0x12345678L) << endl;
  6549. cout << "x: "<< x << endl;
  6550. cout << "x: "<< hex << setfill('0')
  6551. << setw(sizeof(unsigned long)*2)
  6552. << x.to_ulong() << dec << endl;
  6553. cout << "x <<= 6 == " << (x <<= 6) << endl;
  6554. cout << "x >>= 6 == " << (x >>= 6) << endl;
  6555. cout << "85 ==" << bits<SIZE>(85) << endl;
  6556. cout << "x ^ 85 == " << (x ^ 85) << endl;
  6557. cout << "x & 85 == " << (x & 85) << endl;
  6558. cout << "85 & x === " << (85 & x) << endl;
  6559. cout << "~x == " << (~x) <<" == "
  6560. << (~x).to_ulong() << endl;
  6561.  
  6562. y = 0x55555550L;
  6563. cout << "y: " << y << " (" << y.count()
  6564. << " bits set)" << endl;
  6565. cout << "y[0]: " << hex << setfill('0')
  6566.  
  6567. << setw(sizeof(unsigned long)*2)
  6568. << y.to_ulong() << dec << endl;
  6569. cout << "x & y == " << (x & y) << endl;
  6570. cout << "x y == " << (x I y) << endl;
  6571. cout << "x ^ y == " << (x ^ y) << endl;
  6572. cout << "x != y? "<< (x != y) << endl;
  6573. return 0;
  6574. }
  6575.  
  6576. /* Sample Execution:
  6577. Initial x: 00010010001101000101011001111000
  6578. Initial y: 00000000000000000000000000010110
  6579. Initial z: 00010010001101000101011001111000
  6580. Enter new z: 101001000100001000001
  6581. New z: 00000000000101001000100001000001
  6582. z == 1345601
  6583. y == 22
  6584. x:== 305419896
  6585. x == 00010010001101000101011001111000 (13 bits set)
  6586. x == 0x12345678L? 1
  6587. x: 00010010001101000101011001111000
  6588. x: 12345678
  6589. x <<= 6 == 10001101000101011001111000000000
  6590. x >>= 6 == 00000010001101000101011001111000
  6591. 85 == 00000000000000000000000001010101
  6592. x ^ 85 == 00000010001101000101011000101101
  6593. x & 85 == 00000000000000000000000001010000
  6594. 85 & x === 00000000000000000000000001010000
  6595. ~x == 11111101110010111010100110000111 == 4257982855
  6596. y: 01010101010101010101010101010000 (14 bits set)
  6597. y[0]: 55555550
  6598. x & y == 00000000000101000101010001010000
  6599. x y == 01010111011101010101011101111000
  6600. x ^ y == 01010111011000010000001100101000
  6601. x != y? 1
  6602.  
  6603. // End of File
  6604.  
  6605.  
  6606. Listing 9 Implementation of sets of integers
  6607. #if !defined(INTSET_H)
  6608. #define INTSET_H
  6609.  
  6610. #include <iostream.h>
  6611. #include <stddef.h>
  6612. #include "bits.h"
  6613.  
  6614. template<size_t N>
  6615. class Intset
  6616. {
  6617. public:
  6618.  
  6619. // NOTE: The following constructors shouldn't be
  6620. // necessary. The compiler-generated ones should
  6621. // suffice. For some reason, Borland 3.1 requires
  6622. // these (WATCOM does not).
  6623.  
  6624. // Constructors
  6625. Intset();
  6626.  
  6627. Intset(const lntset<N>& is);
  6628.  
  6629. // Set operations
  6630. Intset<N> operator-(const Intset<N>& is) const;
  6631. Intset<N> operator+(const Intset<N>& is) const;
  6632. Intset<N> operator*(const Intset<N>& is) const;
  6633. Intset<N> operator~() const;
  6634. int operator==(const Intset<N>& is) const;
  6635. int operator!=(const Intset<N>& is) const;
  6636. int operator<=(const Intset<N>& is) const;
  6637. int operator>=(const Intset<N>& is) const;
  6638.  
  6639. // Member operations
  6640. int contains(size_t n) const;
  6641. Intset<N>& insert(size_t n);
  6642. Intset<N>& remove(size_t n);
  6643.  
  6644. size_t count() const;
  6645. friend ostream& operator<<(ostream& os, const Intset<N>& is)
  6646. {is.print(os); return os;}
  6647.  
  6648. private:
  6649. bits<N> bitset;
  6650.  
  6651. int subsetof(const Intset<N>& is) const;
  6652. void print(ostream& os) const;
  6653. };
  6654.  
  6655. template<size_t N>
  6656. Intset<N>::Intset()
  6657. {
  6658. bitset.reset();
  6659. }
  6660.  
  6661. template<size_t N>
  6662. Intset<N>::Intset(const Intset<N>& is)
  6663. {
  6664. bitset = is.bitset;
  6665. }
  6666.  
  6667. template<size_t N>
  6668. inline Intset<N> Intset<N>::operator-(const Intset<N> &is) const
  6669. {
  6670. Intset<N> r(*this);
  6671. r.bitset &= ~is.bitset;
  6672. return r;
  6673. }
  6674.  
  6675. template<size_t N>
  6676. inline Intset<N> Intset<N>::operator+(const Intset<N> &is) const
  6677. {
  6678. Intset<N> r(*this);
  6679. r.bitset =is.bitset;
  6680. return r;
  6681. }
  6682.  
  6683. template<size_t N>
  6684. inline Intset<N> Intset<N>::operator*(const Intset<N> &is) const
  6685. {
  6686.  
  6687. Intset<N> r(*this);
  6688. r.bitset& = is.bitset;
  6689. return r;
  6690. }
  6691.  
  6692. template<size_t N>
  6693. inline Intset<N> Intset<N>::operator~() const
  6694. {
  6695. Intset<N> r(*this);
  6696. r.bitset.toggle();
  6697. return r;
  6698. }
  6699.  
  6700. template<size_t N>
  6701. inline int Intset<N>::operator==(const Intset<N> &is) const
  6702. {
  6703. return bitset == is.bitset;
  6704. }
  6705.  
  6706. template<size_t N>
  6707. inline int Intset<N>::operator!=(const Intset<N> &is) const
  6708. {
  6709. return bitset != is.bitset;
  6710. }
  6711.  
  6712. template<size_t N>
  6713. inline int Intset<N>::operator<=(const Intset<N> &is) const
  6714. {
  6715. return subsetof(is);
  6716. }
  6717.  
  6718. template<size_t N>
  6719. inline int Intset<N>::operator>=(const Intset<N> &is) const
  6720. {
  6721. return is.subsetof(*this);
  6722. }
  6723.  
  6724. template<size_t N>
  6725. inline int Intset<N>::contains(size_t n) const
  6726. {
  6727. return bitset.test(n);
  6728. }
  6729.  
  6730. template<size_t N>
  6731. inline Intset<N>& Intset<N>::insert(size_t n)
  6732. {
  6733. bitset.set(n);
  6734. return *this;
  6735. }
  6736.  
  6737. template<size_t N>
  6738. inline Intset<N>& Intset<N>::remove(size_t n)
  6739. {
  6740. bitset.reset(n);
  6741. return *this;
  6742. }
  6743.  
  6744. template<size_t N>
  6745. inline size_t Intset<N>::count() const
  6746.  
  6747. {
  6748.  
  6749. return bitset.count();
  6750. }
  6751.  
  6752. template<size_t N>
  6753. inline int Intset<N>::subsetof(const Intset<N>& is) const
  6754. {
  6755. bits<N> r(bitset);
  6756. r &= is.bitset;
  6757. return bitset == r;
  6758. }
  6759.  
  6760. template<size_t N>
  6761. void Intset<N>::print(ostream& os) const
  6762. {
  6763. os << '{';
  6764. int first_time = 1;
  6765. for (int i = 0; i < N;++i)
  6766. if (bitset.test(i))
  6767. {
  6768. if (!first_time)
  6769. os << ',';
  6770. os << i;
  6771. first_time = 0;
  6772. }
  6773. os<<'}';
  6774. }
  6775.  
  6776. #endif
  6777.  
  6778. /* End of File */
  6779.  
  6780.  
  6781. Listing 10 Tests the Intset class
  6782. //tintset.cpp
  6783. #include <iostream.h>
  6784. #include "intset. h"
  6785.  
  6786. main()
  6787. {
  6788. Intset<16> x, y;
  6789.  
  6790. for (int i = 0; i < 10;++i)
  6791. {
  6792. x.insert(i);
  6793. if (i % 2)
  6794. y.insert(i);
  6795. }
  6796.  
  6797. cout << "x == " << x << endl;
  6798. cout << "y == " << y << endl;
  6799. cout << "y < = x? " << (y <= x) << endl;
  6800. cout << "y >= x? " << (y >= x) << endl;
  6801. cout << "x - y == " << x - y << endl;
  6802. cout << "x + y == " << x + y << endl;
  6803. cout << "x * y == " << x * y << endl;
  6804. cout << "~x == " << ~x << endl;
  6805. cout << "x.contains(2)? " << x.contains(2) << endl;
  6806.  
  6807. cout << "y.contains(2)? " << y.contains(2) << endl;
  6808. cout << "x.count() == " << x.count() << endl;
  6809. cout << "y.count() == " << y.count() << endl;
  6810. return 0;
  6811. }
  6812.  
  6813. /*Output:
  6814. x == {0,1,2,3,4,5,6,7,8,9}
  6815. y == {1,3,5,9}
  6816. y <= x? 1
  6817. y >= x? 0
  6818. x - y == {0,2,4,6,8}
  6819. x + y == {0,1,2,3,4,5,6,7,8,9}
  6820. x * y == {1,3,5,7,9}
  6821. ~x == {10,11,12,13,14,15}
  6822. x.contains(2)? 1
  6823. y.contains(2)? 0
  6824. x.count() == 10
  6825. y.count() == 5
  6826. */
  6827.  
  6828. // End of File
  6829.  
  6830.  
  6831.  
  6832.  
  6833.  
  6834.  
  6835.  
  6836.  
  6837.  
  6838.  
  6839.  
  6840.  
  6841.  
  6842.  
  6843.  
  6844.  
  6845.  
  6846.  
  6847.  
  6848.  
  6849.  
  6850.  
  6851.  
  6852.  
  6853.  
  6854.  
  6855.  
  6856.  
  6857.  
  6858.  
  6859.  
  6860.  
  6861.  
  6862.  
  6863.  
  6864.  
  6865.  
  6866.  
  6867.  
  6868.  
  6869.  
  6870. On the Networks
  6871.  
  6872.  
  6873. Special Issue: USENET Network News Update
  6874.  
  6875.  
  6876.  
  6877.  
  6878. Sydney S. Weinstein
  6879.  
  6880.  
  6881. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, lecturer, author,
  6882. professor, and President of Datacomp Systems, Inc., a consulting and contract
  6883. programming firm specializing in databases, data presentation and windowing,
  6884. transaction processing, networking, testing and test suites, and device
  6885. management for UNIX and MS-DOS. He can be contacted care of Datacomp Systems,
  6886. Inc., 3837 Byron Road, Huntingdon Valley, PA 19006-2320 or via electronic mail
  6887. on the Internet/USENET mailbox syd@DSI.C0M (dsinc!syd for those who cannot do
  6888. Internet addressing).
  6889.  
  6890.  
  6891. Each January I like to provide an overview of networks, both to answer
  6892. questions I am commonly asked throughout the year, and to benefit readers who
  6893. are completely new to networks. This is my fourth anniversary in writing this
  6894. column, and its time to update the prior January columns. In prior years I
  6895. have written about the following subjects: the internet, the Internet, USENET,
  6896. Network News, obtaining a news feed, and obtaining sources from archive sites.
  6897. This year, I go over pretty much the same subjects: some basic definitions and
  6898. information about networks, a description of USENET Network News, and finally,
  6899. instructions for obtaining software from the networks.
  6900. First, a quick overview of my column. "On The Networks" covers articles posted
  6901. to several of the source groups on USENET Network News: comp.sources.games,
  6902. comp.sources.misc, comp.sources.reviewed, comp.sources.unix, comp.sources.x,
  6903. and alt.sources. Each of these groups is like a section of a large electronic
  6904. magazine called USENET Network News. I call USENET Network News a magazine,
  6905. and not a bulletin board, partly because of the way it distributes its news.
  6906. Unlike a bulletin board, where each reader accesses a central machine to read
  6907. the messages, USENET delivers Network News on a subscription basis to each
  6908. computer, and subscribers read the articles locally. In "On the Networks," I
  6909. let you know about some of the new postings to USENET Network News. 
  6910.  
  6911.  
  6912. Some Definitions and Basic Information
  6913.  
  6914.  
  6915. Ok, I bandy about the terms USENET, Internet, and "the net," among others in
  6916. this column. Its time to update the definitions of these items.
  6917.  
  6918.  
  6919. USENET, internets, and the Internet
  6920.  
  6921.  
  6922. USENET, oftentimes referred to as "the net," is a loose collection of
  6923. cooperating computers. In the past, all of USENET ran UNIX, but now with other
  6924. computers and operating systems supporting UUCP or similar transfer protocols,
  6925. USENET computers could be running anything from MS/DOS to VAX/VMS. A computer
  6926. is considered to be on USENET if it communicates via electronic mail to other
  6927. computers on USENET (I realize I've provided a slightly circular definition).
  6928. USENET consists of Electronic Mail, file transfers, and Network News. Most of
  6929. the programs you read about in this column are distributed via Network News.
  6930. If your computer talks to USENET or another computer via some forwarding
  6931. gateway using a protocol other than UUCP, you are on an internet, short for
  6932. inter-network. Being "on an internet" just means that you are using some
  6933. network other than USENET. Note that this internet is spelled with a lower
  6934. case i, and includes the Internet and several other networks such as CSNET and
  6935. BITNET. When you're on an internet, your actual connection to USENET is via a
  6936. gateway computer that talks to both the network you use and USENET.
  6937. The Internet (capital I) is the set of computer networks that are
  6938. interconnected by the TCP/IP protocol and listed in the routing tables
  6939. maintained by the InterNIC. This giant network of networks grew out of the
  6940. Defense Department's ARPANET (Advanced Projects Research Agency Network).
  6941. While USENET sites make phone calls to other computers, sending information in
  6942. a store and forward fashion, the Internet is mostly a set of machines with
  6943. permanent or on-demand connections that allow direct real-time communication
  6944. between any two computers on the network. In addition, Internet lines usually
  6945. run faster than the dial up lines used by UUCP. Most inter-city traffic and
  6946. the vast majority of Network News is now transfered via the Internet.
  6947. The Internet is undergoing rapid change, and any column that attempts to
  6948. describe it is chasing a moving target. In fact, as of the last months of
  6949. 1993, as this column is being written, the Internet is on the verge of a very
  6950. major change: it is moving from the public to the private sector, with NSFNET
  6951. becoming the National Information Superhighway. However, to give a quick
  6952. description of its current structure, I can say that Internet is a set of
  6953. nation- wide backbone links connecting areas of the country at 45Mb/s (million
  6954. bits per second). Connected to those backbone links are many regional networks
  6955. running at speeds between 1.544Mb/s and 45Mb/s. Connected to the regionals are
  6956. individual networks such as the networks at Datacomp Systems, Inc. These
  6957. networks connect to the regionals at between 9.6kbps (on-demand dial-up links)
  6958. through 1.544Mb/s and 45Mb/s (leased line connections).
  6959. Whereas connection to USENET via UUCP usually provides only mail and news, the
  6960. Internet runs the TCP/IP protocol and thus supports news (NNTP, Network News
  6961. Transfer Protocol), mail (SMTP, Simple Mail Transfer Protocol), remote logins
  6962. to any computer on the network on which you have an account telnet), remote
  6963. file transfer (FTP, file transfer protocol), and many real-time on-line search
  6964. engines including Archie, Gopher, and World-Wide-Web. All of these services
  6965. coexist and work in real time.
  6966.  
  6967.  
  6968. Internet and USENET Addressing
  6969.  
  6970.  
  6971. The Internet performs much of the bulk transfer work for USENET; problems
  6972. often occur because Internet and USENET use two different addressing methods.
  6973. Since a large amount of the software mentioned in this column comes from
  6974. USENET or the Internet, it's worthwhile to understand how to format the two
  6975. types of addresses. A UUCP or USENET address is made up of site names
  6976. separated by ! characters, as in uunet!dsinc!syd. If a site wants to mention
  6977. more than one "well known site" to use as a route, it usually lists them in {
  6978. } characters, as in {uunet, decwrl}!dsinc!syd. In this case, you can use
  6979. either uunet!dsinc!syd or decwrl!dsinc!syd. USENET addressing presents a
  6980. problem -- to do it you must know the complete path from your site to the
  6981. destination site. Some systems run programs to help with this routing, and
  6982. USENET's UUCP Mapping Project publishes maps to automate this process.
  6983. However, not all sites have registered to be listed in these maps.
  6984. Registration is free, recommended, and accomplished by sending your entry to
  6985. rutgers!uucpmap. The mapping project continuously updates the maps and
  6986. distributes them via the USENET news group comp.mail.maps.
  6987. On the Internet, all sites have a unique "Fully Qualified Domain Name" which
  6988. is administered by the NIC. My site's domain name is node.DSI.COM, where node
  6989. is the individual computer at my site. Thus my full current address is
  6990. syd@dsinc.DSI.COM, but our mailer, like the mailers at a lot of Internet
  6991. sites, is smart, and knows how to forward the mail to me even if you send it
  6992. to syd@DSI.COM. This feature allows me to move around within the DSI.COM
  6993. domain without having to tell everyone a new address. The Internet does not
  6994. require you to know the path to the site; you only need to know the domain
  6995. name. The domain name is the complete address to that site.
  6996. Now, a word of warning. Mixing both @ and ! in the same address leads to
  6997. trouble. Not everyone follows the standard and processes the addresses
  6998. correctly. Converting sitea!user@DSI.COM to a UUCP address would properly
  6999. result in dsinc!sitea!user. Note that the @ has higher precedence than the !.
  7000. Many sites get this standard wrong, and will cause your mail to bounce (be
  7001. returned to you as undeliverable). Some sites, ours included, allow UUCP mail
  7002. to have addresses including domain names in the ! path, as in
  7003. dsinc!host.domain.type!user. Where allowed, this form of addressing is usually
  7004. more reliable than mixing the ! and @'s.
  7005.  
  7006.  
  7007. Public Domain vs. Freely Distributed Software 
  7008.  
  7009.  
  7010. Lastly, what is Public Domain Software and what is Freely Distributable
  7011. Software? Much of the software described in this column is Freely
  7012. Distributable, in that you pay no licensing fee if you are acquiring it for
  7013. personal use. Some distributors even allow business use of Freely
  7014. Distributable software for no fee. While most software in this column is
  7015. Freely Distributable, almost all of it is not in the Public Domain. If
  7016. software is in the Public Domain, either the copyright has run out and was not
  7017. renewed, or its authors have specifically renounced copyright protection and
  7018. have placed the software in the Public Domain. For most software mentioned in
  7019. this column the copyright to the software is held either by the author or by
  7020. some group. They then give the user rights to use and distribute the software
  7021. for no charge. This practice does not place the software in the public domain.
  7022. You still cannot sell this software, nor pretend that you wrote it. Many of
  7023. the licensing agreements restrict how the software can be used for business
  7024. purposes.
  7025. Freely Distributable software is also different from Shareware, in that
  7026. Shareware developers expect the user to pay a fee if he or she intends to
  7027. continue using the program. Freely distributable software developers do not. 
  7028.  
  7029.  
  7030. USENET Network News
  7031.  
  7032.  
  7033. This column refers to items posted to the source news groups of USENET Network
  7034. News. How do USENET and USENET Network News differ? USENET Network News is a
  7035. subset of the computers on USENET and the internet that agree to exchange one
  7036. or more of the categories of Network News. Currently there are about 8,000
  7037. different news categories, called newsgroups. The newsgroups are broken down
  7038. into several hierarchies. These categories include the traditional major
  7039. hierarchies of news, comp, rec, sci, soc and talk; regional hierarchies such
  7040. as na, usa, ba, pa, nj (and others); and specialized hierarchies such as
  7041. bionet, biz, bit, UNIX-pc, u3b (and others). The major hierarchies are the
  7042. most widely distributed, accessing over a million computers worldwide.
  7043. Regional hierarchies distribute messages of interest only over a particular
  7044. region, such as North America (na), the United States (usa), the San Francisco
  7045. Bay Area (ba), the state of Pennsylvania (pa), or New Jersey (nj), just to
  7046. name a few. The specialized hierarchies serve communities with special
  7047. interests.
  7048. Each hierarchy has its own set of rules, which are enforced by consensus.
  7049. USENET itself has no governing body, just a set of guidelines, that individual
  7050. computer owners or administrators follow as they see fit. This scheme seems to
  7051. work most of the time, as the net runs without too much chaos. There is even a
  7052. hierarchy that runs without rules, called alt.
  7053. You are considered to be a recipient (or to pass) network news if your
  7054. computer subscribes to one or more of the newsgroups in any of the
  7055. hierarchies. Some sites receive only a handful of the groups, some receive
  7056. most, and some receive all. However, we are talking about a large amount of
  7057. information -- over 60 megabytes of new postings every day. This volume is
  7058. growing at about 7% per month. At 60 megabytes per day, each site can keep
  7059. only a small portion of the feed on line at a time, and at that, only a few
  7060. days worth.
  7061. With so much information coming in each day, it would seem like a lot of work
  7062. just maintaining it, or finding something worthwhile to read. It's not that
  7063. bad. The software to run the news system controls itself almost automatically.
  7064. The software includes facilities to send only those groups to which a
  7065. recipient subscribes, as well as expiring old articles to recover space.
  7066. However, to be a major site in the USENET Network News distribution system
  7067. does require a large amount of disk space, and considerable modem time.
  7068. Several of the groups are of interest to readers of this column; these are the
  7069. source distribution groups. Generally, these groups congregate under the comp
  7070. hierarchy in a collection called sources, thus the names comp.sources.unix and
  7071. comp.sources.misc. Originally, comp.sources.unix released Freely Distributable
  7072. sources that were designed to run on UNIX systems. Many of the sources posted
  7073. there now also run on personal computers and other operating systems, but all
  7074. can run on UNIX. Some of the other sources groups in the comp hierarchy
  7075. currently are: amiga -- for software specific to amiga systems, atari.st --
  7076. for atari-specific software, games -- restricted to game software (and game
  7077. software is also restricted to this group), mac -- for Apple mac's, misc --
  7078. general software, not necessarily for UNIX systems, reviewed -- a
  7079. peer-reviewed software source code group, sun -- software specifically for Sun
  7080. Microsystems workstations, and x -- software for the X windowing system.
  7081.  
  7082. Authors submit their sources to a moderator, who is the only person allowed to
  7083. post to the group. The moderator bundles the sources for distribution, checks
  7084. that they are complete, and posts them. The moderator also assigns Volume and
  7085. Issue numbers to each of the postings. A submission might require several
  7086. issues, because an issue is limited to 60K to 100K bytes. The moderator also
  7087. posts periodic indices of the sources posted to his group. Unlike some groups,
  7088. moderated groups post no discussions of software. Moderated groups post only
  7089. software. This restriction gives moderated groups what is called a high
  7090. "signal-to-noise ratio."
  7091. Because of the high "signal to noise ratio" in these groups, some computer
  7092. sites around the world save the sources for future access. These sites are
  7093. called archive sites. Each archive site decides on its own what groups to
  7094. archive and for how long to keep the archives. It's to these archive sites I
  7095. refer you to obtain the sources mentioned in the column. Why to the archive
  7096. sites? Because the individual members of USENET, unless they archive these
  7097. groups, will have deleted the sources to make room for the newer postings,
  7098. usually within a week of the original posting.
  7099.  
  7100.  
  7101. 60M a Day, How Can This Work?
  7102.  
  7103.  
  7104. A small bit of simple math applied to the Network News volumes yields some
  7105. impressive numbers. If your computer exchanges network news with two neighbors
  7106. (a small site), you are receiving 60Mb a day for a full feed, and sending that
  7107. 60Mb each day to the second site. As a participating site in Network news
  7108. broadcasts, you send any article you get from one site to all other sites you
  7109. are connected to that have already not received the article. Now transferring
  7110. 60Mb per day on a 9600 bps phone line (960 characters per second maximum
  7111. speed) requires approximately 36.40 hours on the phone per day. Not possible,
  7112. you will fall behind very quickly. The solution is to send articles in
  7113. compressed batches. The compression reduces the batch sizes by about 50-70%,
  7114. cutting that 36.40 hours to 12-18 hours. Still a big phone bill. How do sites
  7115. cut that down even further? Most big sites run special modems, such as
  7116. V.32bis/V.42bis (at 19,200bps) or Telebit Worldblazers (at 2,250cps), which
  7117. cuts transmission time down by another factor of two, to about 6-9 hours per
  7118. day.
  7119. A major site might exchange news with 20 or more neighbors. How can they do
  7120. that? Several ways -- one is via a whole bank of modems. Another way to send
  7121. data to so many sites is to only exchange partial feeds of selections from the
  7122. list of 8,000 newsgroups. And the last way is via the high speeds offered by
  7123. the Internet.
  7124.  
  7125.  
  7126. Getting Software
  7127.  
  7128.  
  7129.  
  7130.  
  7131. Network News Software
  7132.  
  7133.  
  7134. There are now two current Network News transport software suites: C News and
  7135. INN. 
  7136. The current version of the traditional Network News transport software is
  7137. named C News, not because it is written in C, but because it follows A News
  7138. and B News as the third rewrite of the transport software. C News supports
  7139. transfer of the news articles (the individual messages) between every member
  7140. of the USENET network. C News works best for smaller sites that mostly have
  7141. UUCP feeds, and that feed a limited set of neighbors.
  7142. Larger sites, especially those with Internet connections, generally run
  7143. Internet Network News (INN) from Rich Salz. INN is largely responsible for
  7144. cutting down the time it takes for an article to be propagated throughout the
  7145. backbone networks. Whereas C news uses batching to distribute articles in
  7146. bulk, INN uses an immediate transfer to its NNTP (TCP/IP based network news
  7147. protocol) neighbors. Thus an article on Internet now reaches most of the
  7148. backbone and regional network sites in only one to five minutes. (Just three
  7149. years ago this delay was close to a day). 
  7150.  
  7151.  
  7152. Getting Software Mentioned in This Column 
  7153.  
  7154.  
  7155. Since particular sites keep news articles online for a short period of time
  7156. (usually less than two weeks), by the time a piece of software appears in this
  7157. column, it will have been expired and deleted for a long time. Thus you must
  7158. access a news archive site. Many sites around the country have agreed to
  7159. archive specific news groups. These sites are listed in the comp.archives news
  7160. group. Many of the archive sites also identify themselves in their USENET
  7161. Mapping Project map entry. I have even listed some in this column. How you
  7162. access the archives depends on where they are, and how that site has set up
  7163. access. Most archives allow either FTP or UUCP access and a few even allow
  7164. both.
  7165. If a site supports FTP access, you need to be on the Internet to access it.
  7166. FTP allows you to open up a direct connection to the FTP server on a remote
  7167. system and transfer the files directly to your system FTP will prompt for a
  7168. user name and optionally a password. Most FTP archive sites allow you to enter
  7169. a user name of "anonymous." If such a site then prompts for a password, any
  7170. password will work, but convention and courtesy dictate that you use your name
  7171. and site address for the password.
  7172. If a site supports UUCP access, anyone with UUCP can access the archives. Most
  7173. sites of this type publish a sample entry for the Systems file (L.sys) showing
  7174. the system name, phone number of their modems, the connection speeds
  7175. supported, and the login sequence. Using the uucp command you can poll the
  7176. system directly and retrieve the software. Many sites post times-of-day
  7177. restrictions on when you should access the modems. Courtesy dictates that you
  7178. follow their requests, and some sites enforce the limit with programs. Be sure
  7179. to call far enough before the end of the period to complete your transfer in
  7180. time.
  7181. A third transfer method, used for smaller files, is through access to an
  7182. electronic-mail-based archive server. In this method, you send an electronic
  7183. mail message to the archive server's mailbox name specifying the files you
  7184. wish. The server will return the files to you via electronic mail. Remember
  7185. that many sites limit the size of a single mail message, so don't ask for too
  7186. much at once. Also remember that the archive server is a program, so phrase
  7187. your request exactly as specified in the instructions for that archive server,
  7188. and limit your message to exactly that request. Other comments in the message
  7189. could confuse the program and make it fail to honor your request.
  7190. Lastly, if your site is not connected to any network, some archive sites will
  7191. copy the software onto media for you, if you send them a disk or tape along
  7192. with return postage and a mailer. Other sites sell media with the software
  7193. already copied onto it. This practice is especially useful for the largest
  7194. distributions, such as the X windowing system, which spans multiple tapes.
  7195. If you don't have Internet access, but subscribe to UUNET, UUNET will retrieve
  7196. the files via FTP for you and make them available for UUCP access.
  7197.  
  7198.  
  7199. What to Retrieve
  7200.  
  7201.  
  7202. When I list a package from the newsgroups, I provide five pieces of
  7203. information for each package: The Volume number, Issue(s) numbers, archive
  7204. name, the contributor's name, and the contributor's electronic mail address.
  7205. The Volume and Issue are specifically named in the listing. The archive name
  7206. is in italics, and the contributor's name is followed by his or her electronic
  7207. mail address, enclosed in<>'s.
  7208. To locate a package via WAIS or archie, use the archive name. The archive name
  7209. is the short, one-word name in italics given with each listing. To find the
  7210. file at an archive site, use the group name (from the section of the column
  7211. you are reading -- I place all listings for each group together in the
  7212. column), the volume number, and the archive name. Most archive sites store the
  7213. postings as group/ volume/archive-name. The issue numbers tell you how many
  7214. parts the package was split into when posted. You can use issue numbers to be
  7215. sure to get all of the parts.
  7216. In addition, I report on patches to prior postings. These patches also include
  7217. the volume numbers, issue(s) numbers, archive name, the contributor's name,
  7218. and the contributor's electronic mail address. Patches are stored differently
  7219. by different archive sites. Some sites store patches along with the original
  7220. volume/archive name of the master posting. Some sites store them by the
  7221. volume/archive name of the patch itself. The archive name listed is the same
  7222. for both the patch and the original posting.
  7223. Alt.sources, being unmoderated, does not have volume and issue numbers. So I
  7224. report on the date in the "Date:" header of the posting and the number of
  7225. parts in which it appeared. If the posting was signed an archive-name by the
  7226. contributor, I also report on that archive name. Archive sites for alt.sources
  7227. are harder to find, but they usually store things by the archive name.
  7228.  
  7229.  
  7230. Where to Retrieve Listings
  7231.  
  7232.  
  7233. The problem then, is finding out which sites archive which groups, and how to
  7234. access these archives. I again refer to the articles by Jonathan I. Kames of
  7235. the Massachusetts Institute of Technology, posted to comp.sources.wanted and
  7236. news.answers. These articles appear weekly and explain how to find sources.
  7237. As a quick review, here are the steps:
  7238. I. Figure out in what group, Volume, and Issue(s) the posting appeared. Also
  7239. try and determine its archive name. If you know these items, it's usually easy
  7240. to find an archive site that archives that group. Most archive sites keep
  7241. their information in a hierarchy, ordered first on the group, then on the
  7242. volume number, and last on the archive name. These specifications together
  7243. usually make up a directory path, as in
  7244. comp.sources.unix/volume22/elm2.3
  7245. In that directory you will find all of the articles that made up the 2.3
  7246. release of the Elm Mail User Agent that was posted in Volume 22 of the
  7247. comp.sources.unix newsgroup. If you do not know the archive name, but do know
  7248. the volume, each volume also has an Index file that you can retrieve and read
  7249. to determine the archive name. UUNET is one common publicly accessible archive
  7250. site for each of the moderated groups mentioned in this article.
  7251. II. If you do not know which sites archive the groups, or even if any site is
  7252. archiving a particular item (because they are not archiving the entire group),
  7253. consult Archie. (See "On the Networks," CUJ August 1991, Vol. 9, No. 8).
  7254. Archie is a mail response program that tries to keep track of sites reachable
  7255. via FTP that have sources available for distribution. Even if you cannot
  7256. access the archive site directly via FTP, it is worth knowing that the archive
  7257. site exists because there are other ways of retrieving sources available only
  7258. via FTP. Archie can help you find out if the archive site exists, and where.
  7259. III. If you know the name of the program, but do no know what group it was
  7260. posted in, try using Archie and search for the program based on the name.
  7261. Since most sites store the archives by group and volume, the information
  7262. returned will tell you what newsgroup and volume it was posted in. Then you
  7263. can retrieve the item from any archive site for that newsgroup.
  7264. IV. If you do not even know the name, but know you are looking for source code
  7265. that performs some function, retrieve the indexes for each of the newsgroups
  7266. and see if any of the entries (usually listed as the archive name and a short
  7267. description of the function) look reasonable. If so, try those. Or, make a
  7268. query to Archie based on some keywords from the function of the software, and
  7269. perhaps it can find items that match.
  7270.  
  7271.  
  7272. CD-ROM Archives
  7273.  
  7274.  
  7275.  
  7276. CD-ROMs containing USENET-posted sources, as well as other sources, are also
  7277. available. Two of the larger publishers are Walnut Creek CD-ROM and Prime Time
  7278. Freeware.
  7279. Walnut Creek CD-ROM, 1547 Palos Verdes Mall, Suite 260, Walnut Creek, CA (800)
  7280. 786-9907 or (510) 947-5996 publishes several CD-ROMs each year. Published
  7281. software includes the Simtel20 MS-DOS Archive, the X and GNU archives,
  7282. MS-Windows sources, and other collections of sources and binaries. Disks run
  7283. from $25 to $60 each (varying by title) plus shipping. In addition, Walnut
  7284. Creek offers those hard to find CD-caddys at reasonable prices.
  7285. Prime Time Freeware, Prime Time Freeware, 370 Altair Way, Suite 150,
  7286. Sunnyvale, CA 94086, (408) 738-4832, <ptf@cfcl.com>, publishes twice a year a
  7287. collection of Freely Distributable source code, including the complete USENET
  7288. archives. Prime Time's disks run about $60 each set plus shipping. The latest
  7289. issue, 1993, has over 3 Gb of source code spread over two disks. Prime Time
  7290. also offers a standing subscription plan at a discount. 
  7291.  
  7292.  
  7293. Conclusion
  7294.  
  7295.  
  7296. I hope this special edition of my column has given you a hint as to how to
  7297. read my column and track down the sources. Note: I have been asked many times
  7298. if I can make floppies or tapes containing the software mentioned in my
  7299. column. I cannot spare the time to do this. I also have to work (and teach)
  7300. for a living, and if I started doing this, I could easily spend all my time
  7301. trying to fulfill the requests and never get any of my work done.
  7302. However, what I have offered to do in the past, and am still willing to do, is
  7303. provide a list of USENET sites in your area code. Send me a self-addressed,
  7304. stamped envelope (my address is in the bio squib attached to this column).
  7305. Those living in major metropolitan areas, please include two stamps on your
  7306. letter. Note: I can only offer this service for US area codes. If you have net
  7307. access, but need a news neighbor, I will also reply to Electronic Mail asking
  7308. for nearby news sites.
  7309.  
  7310.  
  7311.  
  7312.  
  7313.  
  7314.  
  7315.  
  7316.  
  7317.  
  7318.  
  7319.  
  7320.  
  7321.  
  7322.  
  7323.  
  7324.  
  7325.  
  7326.  
  7327.  
  7328.  
  7329.  
  7330.  
  7331.  
  7332.  
  7333.  
  7334.  
  7335.  
  7336.  
  7337.  
  7338.  
  7339.  
  7340.  
  7341.  
  7342.  
  7343.  
  7344.  
  7345.  
  7346.  
  7347.  
  7348.  
  7349.  
  7350.  
  7351.  
  7352.  
  7353.  
  7354.  
  7355.  
  7356.  
  7357.  
  7358.  
  7359.  
  7360.  
  7361. CUG New Releases
  7362.  
  7363.  
  7364. C Exploration Tools, GNU Indent, and Miscellaneous Goodies
  7365.  
  7366.  
  7367.  
  7368.  
  7369. Victor R. Volkman
  7370.  
  7371.  
  7372. Victor R. Volkman received a BS in Computer Science from Michigan
  7373. Technological University, He has been a frequent contributor to The C Users
  7374. Journal since 1987. He is currently employed as Senior Analyst at H.C.I.A. of
  7375. Ann Arbor, Michigan. He can be reached by dial- in at the HAL 9000 BBS (313)
  7376. 663- 4173 or by Usenet mail to sysop@hal9k.com.
  7377.  
  7378.  
  7379.  
  7380.  
  7381. Introduction
  7382.  
  7383.  
  7384. This month's new offerings actually spreads five new releases across three CUG
  7385. volumes. Generally, a single CUG volume covers one specific library or
  7386. programming tool. A CUG volume typically consists of from one to ten
  7387. individual diskettes. However, there are some releases for which an entire
  7388. diskette provides way too much space. These releases often have titles like
  7389. "Miscellany #x," reflecting the eclectic nature of the submissions. For small
  7390. releases, the CUG Library will cluster several of them on the same diskette
  7391. (and hence the same volume). This practice both conserves resources and
  7392. provides more value to the end users of the CUG Library.
  7393.  
  7394.  
  7395. New Library Acquisitions
  7396.  
  7397.  
  7398.  
  7399.  
  7400. C/C++ Exploration Tools v2.12: CUG #391
  7401.  
  7402.  
  7403. C/C++ Exploration Tools, by Juergen Mueller (Kornwestheim, Germany), includes
  7404. both his C Function Tree Generator (CFT) and the C Structure Tree Generator
  7405. (CST). CFT and CST analyze the C/C++ source code of applications of any size,
  7406. over multiple files. CFT and CST are useful for exploring new, unknown
  7407. software and for supporting re- use, maintenance and re- engineering of
  7408. software. By preprocessing, scanning, and analyzing the program source code,
  7409. these programs generate the function call hierarchy (CFT) and the data
  7410. structure/class (CST) relations. While both programs can handle C and C++
  7411. code, CFT can also analyze assembly language code.
  7412. An important feature of this product is its database generation, which allows
  7413. you to recall code information without reprocessing the source. You can read
  7414. this database again from CFT and CST to produce different outputs or to add
  7415. new files to the database. The database format is dBASE compatible. Special
  7416. recall programs called CFTN and CSTN perform fast searching for items in the
  7417. database. These programs can be used within any environment, for example, from
  7418. within editors like BRIEF, QEDIT, or MicroEMACS (DOS and Windows version), to
  7419. provide a full software project management system with access to all functions
  7420. and data types with just a keystroke. This feature makes a comfortable
  7421. "hypertext source code browser and locator" system out of the editor.
  7422. The documentation is supplied in ASCII and includes 63 pages of reference
  7423. material. The manual is written in plain English and should be accessible to
  7424. even novice C programmers even though the manual discusses advanced
  7425. techniques.
  7426. The C Exploration Tools v2.12 released 07/03/93) are immediately available as
  7427. MS- DOS executables on CUG volume #391. The C Exploration Tools are shareware
  7428. and require registration with the author if you decide to use them beyond the
  7429. 30- day evaluation period. The registration price is $46 U.S. or 60 DM for a
  7430. single copy. Generous site license discounts with prices as low as $15 are
  7431. appropriate for corporate use or educational institutions. Registered users
  7432. automatically receive Protected Mode versions of the tools optimized for the
  7433. 80386 and the latest versions of everything. Source code for the C Exploration
  7434. Tools is not available.
  7435.  
  7436.  
  7437. GNU Indent v1.8: CUG #392
  7438.  
  7439.  
  7440. GNU Indent, from Joseph Arceneaux (San Francisco, CA), becomes the newest
  7441. installment of high quality tools from the GNU project. The Indent program
  7442. changes the appearance of a C program by inserting or deleting whitespace. The
  7443. Indent program can make code easier to read. It can also convert C code from
  7444. one writing style to another. Indent understands a substantial amount about
  7445. the syntax of C, but it also attempts to cope with incomplete and misformed
  7446. syntax. Indent can replace the original source .C file and retain a backup
  7447. copy or else write its output to a new .C file.
  7448. There are several common styles of C code, including the GNU style, the
  7449. Kernighan & Ritchie style, and the original Berkeley style. You may select a
  7450. style with a single "background" option, which specifies a set of values for
  7451. all other options. However, explicitly specified options always override
  7452. options implied by a background option. Thus, you can create hybrid styles or
  7453. a new coding style uniquely your own by combining the many option settings.
  7454. Option settings cover many things that programmers regularly spar about, such
  7455. as:
  7456. Placement of blank lines, braces, and comments
  7457. Special handling for braces around if/then/else constructs
  7458. Spacing around casts and sizeof
  7459. Overall number of spaces per indentation level
  7460. Alignment of parentheses on continuation lines
  7461. All aspects of function declaration layout
  7462. Each option can be specified in short form or long form. For example, the
  7463. short form option "- ncdb" can be entered as
  7464. "--no-comment-delimiters-on-blank-lines."
  7465. GNU Indent supports MS- DOS, OS/2, VAX VMS, and most versions of UNIX. For
  7466. UNIX versions, Indent includes the popular GNU auto- configuration utility
  7467. which customizes the Makefile to meet the needs of your system. The CUG
  7468. Library distribution includes source code only. GNU Indent v1.8 (released
  7469. 06/16/93) is immediately available as CUG volume #392.
  7470.  
  7471.  
  7472. LL, GIFSave, and Cordic++: CUG #393
  7473.  
  7474.  
  7475. As you might have guessed from the introduction, this volume is something of a
  7476. C potpourri. George Matas (University of Surrey, U.K.) presents his LL for a
  7477. generic double- linked list library with examples. Sverre H. Huseby (Oslo,
  7478. Norway) contributes GIFSave to save bitmaps in this popular image file format.
  7479. Last, Cordic++ by Timothy M. Farnum (Rochester, NY) builds on Michael
  7480. Bertrand's C implementation of fast trigonometric functions. Altogether, these
  7481. are three very useful and specialized tools for common C problems. The entire
  7482. set fits on just one diskette. This diskette is immediately available as CUG
  7483. volume #393.
  7484.  
  7485.  
  7486.  
  7487. LL: CUG#393A
  7488.  
  7489.  
  7490. LL is a double- linked list handler library with more than four dozen operator
  7491. functions. Variables of any type can be stored in an LL list. Individual
  7492. elements of a list may be of different types. With LL, you can create any
  7493. depth of lists of lists. You create an instance of a list using either ConsLL,
  7494. ConsCopyLL, or ConsPtrLL functions. It's best to call one of these functions
  7495. at the point of a list variable's declaration. You must assign the result of
  7496. one of the constructor functions to a given list instance before passing it to
  7497. any other function in the LL library.
  7498. ConsLL creates an empty list. ConsCopyLL(src) creates a new copy of an
  7499. existing list. ConsPtrLL(src) creates a list of pointers to elements stored in
  7500. list src. DestLL(list) destroys a list; i.e. it deletes all elements and frees
  7501. all memory allocated for list. DestLL should be called at the point where list
  7502. goes out of scope.
  7503. LL has been tested only on SUN Sparcstations and DEC Ultrix machines. In these
  7504. environments, LL works with both the native "cc" compiler as well as GNU C
  7505. ("gcc"). The CUG Library distribution includes source only.
  7506.  
  7507.  
  7508. GIFSave: CUG#393B
  7509.  
  7510.  
  7511. The GIFSAVE library enables you to save GIF- images from your own graphics-
  7512. producing C programs. GIFSAVE creates simple GIF files following the GIF87a
  7513. standard. You can't create GIF files from interlaced images, and you should
  7514. store only one image per file.
  7515. GIFSAVE consists of four functions, all declared in GIFSAVE.H:
  7516. GIF_Create creates new GIF files. GIF_Create takes parameters specifying the
  7517. filename, screen size, number of colors, and color resolution.
  7518. GIF_SetColor sets up the red, green, and blue color components. It should be
  7519. called once for each possible color.
  7520. GIF_CompressImage compresses an image. GIF_CompressImage accepts parameters
  7521. describing the position and size of the image on screen, and a user- defined
  7522. callback function that is supposed to fetch the pixel values.
  7523. GIF_Close terminates and closes a GIF file.
  7524. You should call these functions in the listed order for each GIF file. You
  7525. must close one file before you creates a new one. To use these functions, you
  7526. must create a callback function that will retrieve the pixel values for each
  7527. point in the image.
  7528. GIFSAVE includes a makefile for use with Borland C/C++. Huseby claims he has
  7529. taken care to insure that all byte- order sensitive operations are handled in
  7530. a platform- independent method. Therefore, the source code should work without
  7531. modification on non- MS- DOS platforms.
  7532. The Graphics Interchange Format(C) is the Copyright property of CompuServe
  7533. Incorporated. GIF is a Service Mark property of CompuServe Incorporated.
  7534. Huseby has released GIFSAVE as Public Domain source code with no restrictions
  7535. on its use.
  7536.  
  7537.  
  7538. CORDIC++: CUG#393C
  7539.  
  7540.  
  7541. The Coordinate Rotational Digital Computer (CORDIC) was an early device
  7542. implementing fast integer sine and cosine calculations. By favoring integer
  7543. operations over floating point, CORDIC demonstrated a classic computing
  7544. tradeoff of speed vs. precision. Although the CORDIC algorithm was first
  7545. documented by Jack E. Volder in 1959, most CUJ readers may remember Michael
  7546. Bertrand's C implementation (see "The CORDIC Method for Faster sin and cos
  7547. Calculations," CUJ, November 1992). Farnum presents his own reimplementation
  7548. of Bertrand's code -- this time as a full C++ class.
  7549. According to Farnum, the most notable change in his C++ version is his
  7550. encapsulation of variables which were global in the C version. As static
  7551. member variables of a CORDIC class these encapsulated variables become
  7552. protected from accidental modification by routines unaware of them. Moving
  7553. these variables inside a class structure makes it necessary to develop
  7554. interface routines. Farnum decided to predefine one member of the CORDIC
  7555. class, cord, to access the member functions which compute the integer sine and
  7556. cosine. You could create other instances of class cordic that would work as
  7557. well as the predefined instance, but there is little advantage to this in the
  7558. current implementation.
  7559. Ambitious readers could build upon this implementation by providing the
  7560. ability to instantiate the CORDIC class with different levels of accuracy. For
  7561. example, a programmer could provide different levels of accuracy by using
  7562. different bases for the CORDIC algorithm. Farnum decided against this approach
  7563. because the complexity of such an implementation seemed to go against the
  7564. straightforwardness which is the main advantage of the CORDIC algorithm.
  7565.  
  7566.  
  7567.  
  7568.  
  7569.  
  7570.  
  7571.  
  7572.  
  7573.  
  7574.  
  7575.  
  7576.  
  7577.  
  7578.  
  7579.  
  7580.  
  7581.  
  7582.  
  7583.  
  7584.  
  7585.  
  7586.  
  7587.  
  7588.  
  7589.  
  7590.  
  7591.  
  7592.  
  7593.  
  7594.  
  7595.  
  7596.  
  7597.  
  7598.  
  7599. Editor's Forum
  7600. I'm back to being optimistic again, though heaven knows why. I write these
  7601. words shortly after returning from the November 1993 joint meeting of ANSI
  7602. X3J16 and ISO SC22/WG21, the folks standardizing C++. It's not as if I got
  7603. everything I wanted at that meeting -- far from it. But the meeting
  7604. nevertheless represents an important watershed. The public will soon be able
  7605. to get its hands on a reasonably complete, albeit informal, draft of the C++
  7606. standard.
  7607. Four years in the making, produced by a cast of hundreds, this
  7608. mini-spectacular will be in its most complete form ever by about the end of
  7609. January 1994. It will debut as a draft document circulated within ISO SC22,
  7610. the parent committee for all international programming language standards.
  7611. Whatever reviews it garners from its first public exposure will be unofficial
  7612. -- sort of like trying out a Broadway play in Providence or Boston -- but the
  7613. early feedback will be important nonetheless.
  7614. The pressure to produce this informal draft document (it was actually promised
  7615. by December 1993) has been salutary. Andy Koenig, the new project editor, has
  7616. cheerfully divvied up the rather large backlog of unincorporated edits to the
  7617. Working Draft. I won the questionable honor of updating my earlier draft of
  7618. the library portion (see my Editor's Forum, CUJ October 1993) to incorporate
  7619. the latest additions to the C++ library. All this frenzy comes to a head by
  7620. mid January, when Andy and a few friends bring all the bits together in an
  7621. editing bee held at AT&T Bell Labs.
  7622. Pasting the entire draft together for the first time is an important
  7623. milestone, but it is far from the last one. You can bet that changes will be
  7624. adopted at the March 1994 meeting in San Diego CA, and at the July 1994
  7625. meeting in Waterloo ON. The push is on to incorporate all the remaining
  7626. proposed additions before the formal public review process commences. And that
  7627. is supposed to happen for the draft produced after the Waterloo meeting. From
  7628. that point on, you can expect tweaks of steadily diminishing inventiveness, in
  7629. response to public commentary. The process will still take years to result in
  7630. a formal process.
  7631. So why am I optimistic? Because the C Standard went out for informal public
  7632. review in April 1985 and had an immediate stabilizing effect on the entire
  7633. community. It also had a stabilizing effect on the committee itself. People
  7634. felt they knew what Standard C was from that date onward, and they weren't
  7635. keen to see major changes. (In fact, the formal adoption of the C Standard in
  7636. 1989 was an anticlimax.) C++ is much bigger and fuzzier than C, at the
  7637. comparable point of development, but I hope it enjoys similar stability soon.
  7638. That's cause enough for optimism.
  7639. P.J. Plauger
  7640. pjp@plauger.com
  7641.  
  7642.  
  7643.  
  7644.  
  7645.  
  7646.  
  7647.  
  7648.  
  7649.  
  7650.  
  7651.  
  7652.  
  7653.  
  7654.  
  7655.  
  7656.  
  7657.  
  7658.  
  7659.  
  7660.  
  7661.  
  7662.  
  7663.  
  7664.  
  7665.  
  7666.  
  7667.  
  7668.  
  7669.  
  7670.  
  7671.  
  7672.  
  7673.  
  7674.  
  7675.  
  7676.  
  7677.  
  7678.  
  7679.  
  7680.  
  7681.  
  7682.  
  7683.  
  7684.  
  7685.  
  7686.  
  7687.  
  7688.  
  7689.  
  7690.  
  7691.  
  7692.  
  7693.  
  7694. New Products
  7695.  
  7696.  
  7697. Industry-Related News & Announcements
  7698.  
  7699.  
  7700.  
  7701.  
  7702. StratosWare Releases MemCheck v3.0 Professional for DOS
  7703.  
  7704.  
  7705. Stratos Ware Corporation has released MemCheck v3.0 Professional for DOS, an
  7706. error detection and prevention tool for C/C++ programmers. MemCheck v3.0
  7707. Professional detects memory overwrites, memory underwrites, memory leaks, heap
  7708. corruption, stack overwrites, out-of-memory conditions, and other memory
  7709. errors without requiring any source code changes.
  7710. MemCheck v3.0 Professional for DOS checks for memory overwrites and memory
  7711. leaks in all parts of the host application, including third-party vendor
  7712. libraries, with or without source code. Other features include detection of
  7713. stack and static variable ovewrites, and a mouse-driven MemCheck control
  7714. panel. MemCheck v3.0 requires no debugging information and can be used in
  7715. combination with debuggers such as CodeView, Turbo Debugger, and WVIDEO.
  7716. MemCheck v3.0 uses proprietary storage technology which, according to the
  7717. company, makes it run "five to fifty times faster than any other,
  7718. similar-featured debugger."
  7719. MemCheck v3.0 integrates with existing C/C++ code and pops up at run time to
  7720. identify errors by exact file and line number in the source code, or by
  7721. functions called back to main if the error is found in object code. MemCheck
  7722. v3.0 can be linked in or compiled with no source code changes. MemCheck v3.0
  7723. can be switched on or off at run time, and linked out via a "Production"
  7724. library. MemCheck v3.0 Professional for DOS is available for the Microsoft,
  7725. Borland, Intel, Watcom, and MetaWare compilers for $139. For more information
  7726. contact StratosWare Corporation, 1756 Plymouth Rd., Suite 1500, AnnArbor,
  7727. MI48105, (800) 933-3284 or (313) 996-2944; FAX: (313) 747-8519.
  7728.  
  7729.  
  7730. Ready-to-Run Introduces C/C++ Development System for SCO UNIX and Xenix
  7731.  
  7732.  
  7733. Ready-to-Run Software, Inc. has introduced a C/C++ development system for SCO
  7734. UNIX and SCO Xenix systems. This extension to Ready-to-Run's LanguagePak#1
  7735. lets users of SCO and compatible UNIX systems build software independent of
  7736. proprietary development systems. LanguagePak#1 is based on the GNU compiler,
  7737. so ANSI and POSIX-compliant compilers and libraries are available to
  7738. developers. The LanguagePak#1 includes: a set of C libraries, a curses
  7739. library, a Termcap library and associated header files, and GNU C and C++
  7740. compilers. The package also includes the GNU C++ Class Library, and the GNU
  7741. gdb debugger. LanguagePak#1 is available both as executable flies and as
  7742. source code.
  7743. The LanguagePak#1 is one of Ready-to-Run's ReadyPak line of software products,
  7744. which are built into finished, ready-to-run versions for specific UNIX
  7745. environments, eliminating the need for a find/download/build/test/install
  7746. process on the user's part. The LanguagePak#1 is $275. For more information
  7747. contact Ready-to-Run, Rustic Trail, Groton, MA 01450, (508) 448-3959 or (800)
  7748. 743-1723; FAX: (508) 448-2989; e-mail: info@rtr.com.
  7749.  
  7750.  
  7751. AIB Upgrades SENTINEL
  7752.  
  7753.  
  7754. AIB Software, Inc., formerly Virtual Technologies, has upgraded its SENTINEL
  7755. debugging environment for C/C++ and X-Window developers. SENTINEL v2.0 has
  7756. added a graphical user interface and will be integrated with Hewlett-Packard's
  7757. Soft-Bench development environment. Softbench is a C/C++ programming
  7758. environment built on the framework for open, integrated CASE. SENTINEL is a
  7759. library of routines that can be linked into UNIX C/C++ programs to help
  7760. programmers locate and resolve hidden bugs in the use of dynamic memory.
  7761. Providing run-time verification of pointer usage and dynamic memory
  7762. allocation, SENTINEL traps memory errors, traces stack, and reports the source
  7763. file, function name, and line number of offending statements. SENTINEL also
  7764. gives developers the same level of information concerning the allocation of
  7765. memory and where the memory was freed or overwritten.
  7766. SENTINEL v2.0 supports HP's Soft-Bench on HP and Sun platforms, and IBM's
  7767. implementation of the system on the RS/6000. SENTINEL's GUI will be available
  7768. on currently supported platforms.
  7769. Users will have their choice of three licensing options for SENTINEL v2.0:
  7770. host-based licensing, which will let any number of users on the host access
  7771. SENTINEL; floating network licensing which will provide licensed access to any
  7772. one user on a network at one time; and floating registered use licensing which
  7773. will designate one person per site by name as the licensed user. SENTINEL v2.0
  7774. ranges from $595 to $1895, and pricing is platform specific.For more
  7775. information contact AIB Software, Inc., 46030 Manekin Plaza, Suite 160,
  7776. Dulles, VA 20166, (800) 296-3000 or (703) 430-9247; FAX: (703) 450-4560;
  7777. e-mail: info@aib.com.
  7778.  
  7779.  
  7780. Liant Upgrades C++/Views
  7781.  
  7782.  
  7783. Liant Software has upgraded its object-oriented development tool C++/Views.
  7784. C++/Views v3.0 is a third-generation, object-oriented development tool that
  7785. combines 100 ready-to-use classes with programmer productivity tools. This
  7786. library includes interface, data, event, printer, and extended GUI classes.
  7787. C++/Views uses the local GUI's toolkit whenever possible, allowing programmers
  7788. to create native GUI applications. Features on C++/Views v3.0 include
  7789. C++/Views Constructor, C++/Views Browser, and geometry management.
  7790. C++/Views Constructor lets developers work visually with the C++/Views class
  7791. library. C++/Views combines a visual interface builder with a browser,
  7792. allowing users to switch between drawing and archiving portable resources to
  7793. edit code which call these resources. The C++/Views Interface Builder is a
  7794. WYSIWYG editing tool for designing and testing the behavior of portable
  7795. resources (binary files of GUI objects such as bitmaps, dialogs, and menus).
  7796. Portable resources are called from an application at run time. The same
  7797. resource file can be called from Windows, Motif, Presentation Manager,
  7798. Macintosh, or DOS applications. The C++/Views Constructor also lets users view
  7799. and edit C++ code as they work within Constructor's object-oriented
  7800. environment.
  7801. The C++/Views Browser is a multiple document interface (MDI) application,
  7802. which allows users to open and cut-and-paste among multiple C++ applications;
  7803. view the class hierarchy; edit, inherit, add, and delete classes; and create
  7804. and update header files, make files, and linker response files.
  7805. C++/Views v3.0 also automatically adjusts the geometry of GUI objects so that
  7806. the objects stay in their correct proportion and location when an application
  7807. is moved to other platforms, or when the windows are resized. The API of
  7808. C++/Views v3.0 includes a set of standard interface classes for designing MDI
  7809. application windows. C++/Views v3.0 ranges in price from $149 for an upgrade
  7810. to $2,999 for multi-platform suites. For more information contact Liant
  7811. Software Corporation, 959 Concord St., Framingham, MA 01701, (508)872-8700;
  7812. FAX: (508)626-2221.
  7813.  
  7814.  
  7815. Imperial Software Adds Converter to X-Designer
  7816.  
  7817.  
  7818. Imperial Software Technology has added a converter to X-Designer, its Motif
  7819. GUI builder. The converter enables X-designer to convert OPEN LOOK interface
  7820. designs to Motif by reading the .g files used by Sun DevGuide as its savefile
  7821. format and converting them to the equivalent .xd files used by X-Designer. The
  7822. .xd files can then be loaded into X-Designer to display the Motif interface.
  7823. The user can make final adjustments or polish the Motif interface using
  7824. X-Designer before generating the interface code in C, C++ or UIL.
  7825. The OPEN LOOK to Motif converter upgrade is available to existing X-Designer
  7826. users on Sun under their maintenance agreement, and will ship to all new Sun
  7827. X-designer customers as part of the standard product. The converter runs on
  7828. SunOS 4 and SPARC Solaris 2. For more information contact Imperial Software
  7829. Technology, 95 London St., Reading, Berks RG1 4QA, United Kingdom,
  7830. +44-734-587055; FAX: +44-734-589005. U.S. distributor for X-Designer is the
  7831. V.I. Corporation, Northampton, MA, (800) 732-3200.
  7832.  
  7833.  
  7834. Lucid Ships Energize v2.1
  7835.  
  7836.  
  7837. Lucid, Inc. has begun shipping Energize v2.1, an upgrade to Energize, their
  7838. integrated, incremental programming system for C/C++. Energize supports a set
  7839. of tools for code construction, editing, compiling, debugging, building
  7840. facilities, and a set of browsers for code understanding and reverse
  7841. engineering.
  7842. Features of Energize v2.1 include: C++ templates support; pre-compiled headers
  7843. capability; source control system integration; syntax highlighting features;
  7844. and Makefile integration. Other features include: menu-driven integration with
  7845. source control systems, including SCCS, RCS, and CVS; support for the use of
  7846. Makefiles with a shell utility; and syntax highlighting features that allow
  7847. users to assign colors and fonts to syntactic constructs in source files.
  7848. Three compiler options have been added for cfront compatibility.
  7849. Lucid also announced the integration of Energize v2.1 with Pure Software's
  7850. performance analysis software, Quantify. With Quantify, programmers can
  7851. analyze the performance characteristics of their projects and identify
  7852. bottlenecks in their code.
  7853. Energize v2.1 is available for Sun SPARCstations and compatibles running SunOS
  7854. v4.x (Solaris v1.0). Energize v2.1 is $16,250 for a workgroup of five or
  7855. $29,500 for a workgroup of 10. Volume discounts and site licenses are also
  7856. available. For more information contact Lucid, Inc., 707 Laurel St., Menlo
  7857. Park, CA 94025, (415) 329-8400; FAX: (415) 329-8480.
  7858.  
  7859.  
  7860. IDE Ships C++DE on IBM RS/6000
  7861.  
  7862.  
  7863.  
  7864. Interactive Development Environments, Inc. (IDE) has begun shipping Software
  7865. through Pictures C++ Development Environnment (C++DE) designed for the IBM
  7866. RS/6000 workstations and servers. C++DE is an integrated, multi-user,
  7867. object-oriented software development tool that supports design components.
  7868. The C++DE for the RS/6000 integrates two IDE graphical editors supporting
  7869. architectural and detailed design, an interactive reuse browser, and code
  7870. generator for C++ with IBM's C++ POWERbench programming environment. The
  7871. POWERbench intergration uses IBM's SDE WorkBench/06000 to facilitate
  7872. communication between the tools. Interfaces to FrameMaker or Interleaf
  7873. technical publishing systems are also available through IDE's shared
  7874. repository.
  7875. C++DE's graphical editors are driven by Object-Oriented Structured Design
  7876. (OOSD) notation which specifies the drawing and connection rules. C++DE
  7877. supports a C++ specific instantiation of the OOSD notation that provides an
  7878. abstract representation of the language.
  7879. The two graphical design editor components of the C++ Development Environment
  7880. are available on the RS/6000 separately or as an integrated editor set. A
  7881. C++DE five seat Success Package is $75,000 which includes both editors,
  7882. training, consulting, technical support and one year of maintenance.
  7883. Individual C++DE editor sets range from $4,000 to $13,000 per user. For more
  7884. information contact Interactive Development Environments, Inc., 595 Market
  7885. St., 10th Floor, San Francisco, CA 94105, (800) 888-4331; FAX:(415) 543-0145.
  7886.  
  7887.  
  7888. XVT Announces XVI-DS/XVT-DS++ and PowerObjects
  7889.  
  7890.  
  7891. XVT Software Inc. has announced XVT Development Solution for C (XVT-DS) and
  7892. XVT Development Solution for C++ (XVT-DS++). These products repackage XVT's
  7893. software to emphasize visual layout and prototyping of portable graphical user
  7894. interfaces, for C and C++ cross-platform applications. New functions in the
  7895. tools address issues such as supporting multiple platforms, screen sizes, and
  7896. handling different languages. XVT-DS bundles XVT-design v3.0 with the XVT
  7897. Portability Toolkit v4.0. (XVT-DS++ is similar bundling for C++.)
  7898. XVT-Design new features include: GUI Object Palette and Layout Toolbar, Bitmap
  7899. Editor, and GUI Object Browser, XVT Portability toolkit enhancements include:
  7900. geometry management, portable bitmaps, internationalization, Help System, and
  7901. custom control enhancements.
  7902. XVT also announced PowerObjects, custom controls that a developer can
  7903. incorporate into a user interface, complementing XVT's GUI development tools.
  7904. PowerObjects include: a table object, a spreadsheet object, a toggle/picture
  7905. button object, a tool bar object, and a status bar. PowerObjects for C was
  7906. scheduled to begin shipment in October 1993, while XVT-PowerObjects for C++ is
  7907. slated for 1994.
  7908. XVT products are priced on a developerseat basis, with no additional user
  7909. licensing or royalties. XVT-DS and XVT-DS++ are $1,950 on personal computers
  7910. and $6,300 on workstations. The PowerObjects library is $395 on personal
  7911. computers and 495 on workstations. For more information contact XVT Software
  7912. Inc., 4900 Pearl Fast Circle, Boulder, CO 80301, (303) 445,4223; FAX:
  7913. (303)443-0969.
  7914.  
  7915.  
  7916. Intel and BSO/Tasking Release ANSI C Compiler Toolkit
  7917.  
  7918.  
  7919. Intel Corporation and BSO/Tasking have released an ANSI C Compiler Toolkit
  7920. supporting the Intel MCS-96 family of micro-controllers, including 87C196NT
  7921. and 87C196NQ 20-bit extended derivatives. The agreement between Intel and
  7922. BSO/Tasking gave BSO/Tasking a master source code license to Intel's MCS-96
  7923. Software Development Tools. The MCS-96 ANSI C Compiler Toolkit, hosted on
  7924. PC/DOS, also supports a previously released 16-bit version of the 196 family.
  7925. The MCS-96 ANSI C Compiler toolkit consists of an ANSI C Compiler, Macro
  7926. Assembler, Locating Linker, Librarian, an extended Floating Point Library, and
  7927. other utilities operating in a PC/DOS environment. Intel's ApBUILDER and
  7928. ProjectBUILDER programming and development products are also included in the
  7929. toolkit.
  7930. BSO/Tasking is offering a price discount to all existing InteliC-96 users who
  7931. purchase the MCS-96 ANSI C Compiler Toolkit through March 1994. For more
  7932. information contact Boston Systems Office/Tasking, Norfolk Place, 333 Elm St.,
  7933. Dedham MA, 02026, (617)320-9400; FAX: (617)320-9212.
  7934.  
  7935.  
  7936. McCabe Announces McCabe Tool Set v4.0
  7937.  
  7938.  
  7939. McCabe & Associates have announced McCabe Tool Set v4.0, a reverse engineering
  7940. and testing software tool. McCabe Tool Set v4.0 includes a Data Tool,
  7941. enhancements to CodeBreaker, and a link to desktop publishing packages.
  7942. The Data Tool, which will be incorporated into the core Battlemap tool.
  7943. supports two McCabe data metrics: Global and User-Specified Data Complexity.
  7944. Global Data Complexity measures the Cyclomatic complexity of a module's
  7945. structure as it relates to global/parameter data. The User-Specified Data
  7946. Complexity provides the same measures for a portion of the data. Data Tool
  7947. also supports C/C++; shows data slices; highlights modules and creates classes
  7948. of modules containing specified data; displays declaration and references of
  7949. data; allows editing of source code containing data elements; generates
  7950. reports based on current use specification; and generates global and specified
  7951. data complexity, flow graphs, test paths, and data slices for modules in the
  7952. current search list.
  7953. The CodeBreaker, a tool to find redundant and reusable code, has been paired
  7954. with BattlePlan, McCabe's forward engineering tool. CodeBreaker compares
  7955. module paths, as well as a number of metrics and properties such as names,
  7956. interfaces, design boxes, SPEC notes, and code; generates source code
  7957. templates to match design; identifies existing code that can be reused;
  7958. searches for pre-existing implementation of a design; compares program
  7959. implementation with design; identifies reusable code by comparing a design
  7960. description of what a module should do against physical copies of source code;
  7961. and finds likely candidates of pseudocode that match a set of implemented code
  7962. to reestablish the traceability of the code with its design. CodeBreaker
  7963. includes user-configurable properties, and builds pre-parsed "repositories"
  7964. that can be configured and loaded into CodeBreaker.
  7965. McCabe Tool Set v4.0 can convert tool output to a format that can be imported
  7966. to desktop publishing packages such as Interleaf and FrameMaker. For more
  7967. information contact McCabe & Associates, 5501 Twin Knolls Rd., Suite 111,
  7968. Columbia, MD21045, (800)638-6316; FAX: (410)995-1528.
  7969.  
  7970.  
  7971. PostModern Releases NetClasses v2.0
  7972.  
  7973.  
  7974. PostModern Computing Technologies Corporation has released NetClasses v2.0.
  7975. NetClasses is a set of C++ class libraries for distributed object-oriented
  7976. communications. By linking the appropriate NetClasses libraries, application
  7977. programmers can transport objects over a network, set up fault-tolerant
  7978. peer-to-peer TCP connections, and perform Remote Method Invocation (RMI). The
  7979. programmer can use C++ class library abstraction of TCP, UDP, and file I/O
  7980. streams to communicate objects in connection-oriented, connectionless, and
  7981. persistent object application domains.
  7982. Prasad Mokkapati, PostModern's VP of Engineering describes the changes as
  7983. follows: "In the new release, the agent and classes have been opened up to
  7984. make more information available to the user. We've providing blocking on
  7985. replies, asynchronous and synchronous RMI, and detection of deadlocks and
  7986. stack overflows." Although NetClasses v2.0 drops internal reliance on NIH
  7987. classes, external NIH data is still supported. NetClasses v2.0 includes TCP-
  7988. and UDP-based object transport mechanisms. The TCP and UDP facilities are
  7989. organized as C++ class libraries. For more information contact PostModern
  7990. Computing Technologies, Inc. 1032 Elwell Ct., Suite #240, Palo Alto, CA 94303,
  7991. (415) 967-6169; FAX: (415)967-6212.
  7992.  
  7993.  
  7994. Nu-Mega Upgrades BOUNDS-CHECKER for Windows
  7995.  
  7996.  
  7997. Nu-Mega Technologies, Inc. has upgraded its debugging tool for Windows
  7998. applications. BOUNDS-CHECKER for Windows (BOUNDS-CHECKER/W) v2.0 combines
  7999. Event Logging and Viewing with its automatic bug detection. The most recent
  8000. events, including API calls, window and dialog box messages, hooks, callbacks,
  8001. and ToolHelp notifications, are saved in memory and can be viewed with
  8002. BOUNDS-CHECKER's event log windows. For more analysis, events can be logged to
  8003. a file and viewed with TVIEW, Nu-Mega's trace file viewer. TVIEW uses graphics
  8004. and color to let programmers view the flow of their programs.
  8005. Other features of BOUNDS-CHECKER/W v2.0 include validation of APIs, API return
  8006. value checking, targeted program checking, and a variable and structure
  8007. inspection window. The inspection windows let programmers view data items such
  8008. as arrays, structures, and C++ classes. BOUNDS-CHECKER/W v2.0 also has an
  8009. External Load option that lets programmers select which executable is to be
  8010. checked. BOUNDS-CHECKER/W 2.0 is $249. Upgrades from v1.0 to v2.0 are $69. A
  8011. corporate license program is also available. For more information contact
  8012. Nu-Mega Technologies, Inc. P.O. Box 7780, Nashua, NH 03060, (603) 889-2386;
  8013. FAX; (603)889-1135.
  8014.  
  8015.  
  8016. MicroSoft Releases Visual C++ 32-bit Edition For Windows NT
  8017.  
  8018.  
  8019. Microsoft Corporation has released the Microsoft Visual C++ development system
  8020. 32-bit edition for Windows and Windows NT. This Windows NT-hosted development
  8021. environment targets Win32 and Win32's applications. The new compiler is a
  8022. retail release, and replaces the command-line compiler and tools shipped with
  8023. the Win32 SDK. The new development environment will be distributed via CD-ROM.
  8024. Visual C++ 32-bit Edition provides an integrated set of graphical tools for
  8025. creating Windows applications. The Microsoft Foundation Class v2.0 provides
  8026. encapsulated building blocks. AppWizard supports creation of a "skeleton"
  8027. application that exploits the building blocks. ClassWizard supports
  8028. connections of visual user-interface elements with application code. AppStudio
  8029. supports creation, editing, and browsing of application resources. Visual
  8030. Workbench provides an integrated editor, debugger, browser, and code profiler.
  8031. Visual C++ for Windows NT provides background compilation and multitasking
  8032. with other applications. The product was written from the ground up to be a a
  8033. 32-bit application, using OS features such as multithreading. The debugger now
  8034. includes support for multiplethreads, exception-handling, and a memory window.
  8035. A new analysis tool, SPY++, is included. SPY++ provides information on
  8036. threads, processes, messages, and windows.
  8037. Through an agreement with Chinon America, Microsoft has arranged promotional
  8038. pricing for Visual C++ 32-bit Edition and the Chinon 535 CD-ROM. Microsoft
  8039. describes the bundling agreement as providing up to $300 off the separate
  8040. suggested retail pricing of both. This offer will be valid through February
  8041. 28, 1994, or while quantities last.
  8042. Microsoft also announced license agreements for its Microsoft Foundation Class
  8043. Library C++ application framework v2.0 to other vendors, including competing
  8044. C/C++ compiler vendors, through its Windows Partners Program. The first three
  8045. companies to license the MFC library are Symantec Corporation, MetaWare
  8046. Incorporated, and Blue Sky Software.
  8047. For more information contact Microsoft Corporation, One Microsoft Way,
  8048. Redmond, WA 98052, (206) 882-8080; FAX: (206) 936-7329; Telex: 160520.
  8049.  
  8050.  
  8051. Rational Systems Upgrades DOS/4GW and Instant-C
  8052.  
  8053.  
  8054.  
  8055. Rational Systems, Inc. has released DOS/4GW Professional, the DOS extender
  8056. included with the WATCOM C/C++ and FORTRAN 77 programming languages, and
  8057. Instant-C v5.4, a C development tool. DOS/4GW Professional adds the ability to
  8058. bind the DOS extender to an application. According to the company, programs
  8059. using the DOS/4GW virtual memory manager will load and run two to five times
  8060. faster and DOS/4GW Professional is approximately 50Kb smaller in memory and on
  8061. disk than DOS/4GW.
  8062. Instant-C v5.4 includes enhanced debugging power which, according to
  8063. Rational's release, detects over 700 distinct syntax and run-time errors;
  8064. addition of a "locals" window; context-sensitive online help; and tool-access
  8065. hotkeys and window controls. Instant-C v5.4 also improves object code support
  8066. by displaying register and stack values, and assembly source for object code
  8067. functions. DOS/4GW Professional is $298 and is royalty-free. Instant-C v5.4 is
  8068. $2,995. For more information contact Rational System, Inc. 220 N. Main St.,
  8069. Natick, MA 01760; (508)653-6006; FAX; (508)655-2753.
  8070.  
  8071.  
  8072. Cygnus Support Announces Partnership with Advanced Micro Devices
  8073.  
  8074.  
  8075. Cygnus Support has announced a partnership with Advanced Micro Devices, Corp.
  8076. to provide a development solution for the Am 29205 evaluation board. The
  8077. complete GNU C and C++ software development toolkit from Cygnus Support is
  8078. shipped with each Am29205 evaluation kit. The GNU software development toolkit
  8079. includes the GNU C and C++ compilers, GNU debugger, assembler, linker, and a
  8080. set of binary utilities. Complete documentation for the GNU tool, hosted on
  8081. Sun SPARC and DOS targeting the AMD 29K, is included with each evaluation
  8082. board. GNU tools hosted on Sun SPARC are provided on CD-ROM. GNU tools hosted
  8083. on DOS are provided on 3.5" floppy disks.
  8084. The evaluation kit is available from AMD at $595. Support services for the
  8085. evaluation kit are available from Cygnus Support. For more information contact
  8086. Cygnus Support, 1937 Landings Dr., Mountain View, CA 94043, (415)903-1400;
  8087. FAX: (415)905-0122.
  8088.  
  8089.  
  8090. KL Group Releases XRT/3d v2,0
  8091.  
  8092.  
  8093. KL Group Inc. has upgraded XRT 3/d, a three-dimensional graph widget toolkit
  8094. for X-Window Systems that can represent three-dimensional data in a variety of
  8095. graph types including surfaces, bar charts, and contour graphs. Features of
  8096. XRT/3d v2.0 include the addition of 3-D bar charts and histograms, interactive
  8097. real-time rotation preview, direct labeling of the X, Y and Z axes, CGM
  8098. output, and data formats that can handle irregular gridded data sets.
  8099. XRT/3d uses the same object-oriented API as the OSF/Motif. XRT/3d v2.0 is
  8100. published on the XRT Product CD which contains all XRT widgets for nine UNIX
  8101. architectures: DECStation, DEC Alpha/OSF, IBM, HP 300/400 and 700/800, SVR4,
  8102. Sun, SCO, and SGI. XRT/3d v2.0 is $2,495, and there are no royalties or
  8103. runtime fees. For more information contact KL Group Inc., 134 Adelaide St. E,
  8104. Suite 204, Toronto, Ontario, Canada M5C 1K9, (416) 594-1026; FAX: (416)
  8105. 594-1919.
  8106.  
  8107.  
  8108. Subtle Software Announces Subtleware for C++/SQL
  8109.  
  8110.  
  8111. Subtle Software, Inc. has announced Subtleware for C++/SQL, scheduled to begin
  8112. shipping in the fourth quarter '93. C++/SQL is a bridge technology between the
  8113. object-oriented C++ programming language and the relational database
  8114. management systems (RDBMS). C++/SQL is for developers using C++ and its
  8115. object-oriented constructs for application modeling and development, but who
  8116. also require the use of a SQL/RDBMS for information storage. C++/SQL automates
  8117. the semantic mapping and the coding required to combine Object-Oriented/C++
  8118. and Relational/SQL.
  8119. C++/SQL supports many C++ compiler/preprocessors on a variety of platforms and
  8120. local, remote, or cross-platform RDBMSs. Since C++/SQL generates source code,
  8121. a user can customize the actual bridging code to their requirements. C++/SQL
  8122. provides a process and framework focused on the task of mapping C++ objects to
  8123. and from SQL tables and relations and lets the customer define their own C++
  8124. development enviroment. For more information contact Subtle Software, Inc., 1
  8125. Albion Rd., Billerica, MA 01821, (508) 663-5584.
  8126.  
  8127.  
  8128.  
  8129.  
  8130.  
  8131.  
  8132.  
  8133.  
  8134.  
  8135.  
  8136.  
  8137.  
  8138.  
  8139.  
  8140.  
  8141.  
  8142.  
  8143.  
  8144.  
  8145.  
  8146.  
  8147.  
  8148.  
  8149.  
  8150.  
  8151.  
  8152.  
  8153.  
  8154.  
  8155.  
  8156.  
  8157.  
  8158.  
  8159.  
  8160.  
  8161.  
  8162.  
  8163.  
  8164. We Have Mail
  8165. Code Disk Update
  8166. The C Users Journal provides all code listings from articles on a monthly code
  8167. disk, which may be purchased separately from the magazine or on a subscription
  8168. basis. In addition, the code disk typically contains listings that are too
  8169. long to be printed in the magazine. Unfortunately, several code listings
  8170. referenced in the October 93 CUJ didn't make it to the code disk. These files
  8171. are described as follows:
  8172. splash.zip -- this file contains the entire splash class library described by
  8173. Jim Morris in his article, "The SPLASH Class Library." Due to size
  8174. constraints, this library was not listed in its entirety in the article, but
  8175. was to be provided on the code disk.
  8176. winroth.zip -- this file contains the exception handling macros described by
  8177. Harald Winroth and Matti Rendahl in their article, "Exception Handling in C."
  8178. 1110072C -- this file contains Listing 6 for the article "Random Event
  8179. Simulation for C Programmers" by Martin Scolnick.
  8180. We provide the missing files on the December 1993 code disk. However, if you
  8181. received an October 1993 code disk missing these files, and would like a
  8182. replacement, please call, write, or e-mail R&D Publications, Customer
  8183. Relations, 1601 W. 23rd Suite #200, Lawrence, KS. 66046-2700. (913)-841-1631.
  8184. e-mail: pam@rdpub.com.
  8185. Also, CUJ code listings are available online, from a variety of sources. For a
  8186. description of these sources, refer to the section entitled "CUJ Online Source
  8187. Code," in the Table of Contents, page 6.
  8188. Dear Bill:
  8189. Lint for C++? A Great Idea!
  8190. Actually, as the producers of PC-lint, we've been asked this question hundreds
  8191. of times over the past few years. Our answer has always been, "We're working
  8192. on it. It's coming."
  8193. Well, it's almost here. We are nearing the end of our beta test cycle and on
  8194. November 1, 1993, we expect to release PC-lint 6.00 for C/C++. FlexeLint for
  8195. C/C++ will follow shortly thereafter.
  8196. We appreciate Ken Pugh's recommendation of our PC-lint for C but challenge his
  8197. assumption that the design of C++ all but makes lint-like checking obsolete.
  8198. Over the years, a number of authors have offered do's and don't's for good C++
  8199. programming. See, for example, Effective C++ by Scott Meyers, C++ Programming
  8200. Style by Tom Cargill, C++ Programming Guidelines by Tom Plum and Dan Saks, and
  8201. "Check List for Class Authors" (The C++ Journal, Nov. 92) by Andrew Koenig. At
  8202. the very least, wouldn't it be great to be able to do this kind of checking
  8203. automatically?
  8204. Perhaps a design feature of C++ was to make lint-like checking obsolete, but,
  8205. as they say, the best laid plans of mice and men oft go astray. I am reminded
  8206. of an "unsinkable" ship called The Titanic.
  8207. Sincerely,
  8208. Anneliese Gimpel
  8209. Marketing Director
  8210. Gimpel Software
  8211. 3207 Hogarth Lane
  8212. Collegeville, PA 19426
  8213. (215) 584-4261
  8214. FAX (215) 584-4266
  8215. I am pleased to see the Gimpels moving in this direction. I agree that their
  8216. excellent C tools also have a place in the C++ world.
  8217. Dear Sir,
  8218. Given all the interest in calendar matters, I would like to add some more on
  8219. the subject. First and foremost, I would like to point out the wealth of
  8220. algoritmic information in:
  8221. Doggett, L.E.: "Calendars"
  8222. in Seidelmann, P.K. (ed.):
  8223. Explanatory Supplement
  8224. to the Astronomical Almanac
  8225. Mill Valley, University Science Books, 1992,
  8226. ISBN 0-935702-68-7
  8227. chapter 12 (pps. 575-608).
  8228. One can find there many date and calendar conversion algorithms, from and to
  8229. Julian, Gregorian, Islamic, Indian, and the basic JDN (Julian Day Number),
  8230. with the interesting aspect that not a single algorithm requires the
  8231. floating-point format. A wealth of references is also to be found (51, one of
  8232. them dated 1583! In Latin, of course.) It is to regret that the reference list
  8233. doesn't seem to comply with ISO 690 and therefore some of the books will be
  8234. hard to retrieve.
  8235. It also is interesting to note that astronomers are moving their Julian
  8236. calendar origin from January 1 --4712 into the future, to (the noon of)
  8237. January 1, 2000 (o,c., section 1.253 p. 8) and calling that new system J2000.
  8238. (All this is very much oversimplified here, of course.) That has several
  8239. reasons, but the one that really interests me is avoiding, well, astronomical
  8240. integers. Unfortunately it is connected with a rather arcane thing called
  8241. Barycentric Dynamical Time I had better not know about. Maybe more practical
  8242. will be the modified Julian date (MJD) given by:
  8243. MJD = (Julian day number) - 2400000
  8244. which works with UTC (l.c.) so that January 1, 2000 (noon), which is JD
  8245. 2451545, becomes a quite manageable MJD 51545 (or J2000 day 0).
  8246. There also was a letter from Mr. Viscogliosi to PC Magazine (May 11, 1993, p.
  8247. 401) including a code listing (DOWGJ.C) for Gregorian and Julian day of the
  8248. week (on PC Mag Net too, I suppose). Sorrily, that program should be named
  8249. DOWGJ.CPP -- people are getting confused -- but it's worth being looked at.
  8250. The LeapYear function used by many, and namely presented by Mr. David Burki in
  8251. "Date Conversions," CUJ (1993) 11-2:29-34, works nice as a macro such as:
  8252. #define LeapYear(Y) (! (Y % 4)
  8253. && (Y % 100)
  8254.  !(Y % 400))
  8255. (I'm using TC 2.0.) There's a most interesting Easter Day algorithm presented
  8256. in:
  8257. Carmony, L.A., Holliday, R.L.:
  8258. A First Course in Computer Science
  8259. with Turbo Pascal
  8260. New York. Freeman & Co. 1991. p. 204
  8261. ISBN 0-7167-8216-2
  8262. which is said to have a domain of Year: [1900; 2099] although the reason is
  8263. unstated. After streamlining and translating it boils down to the following
  8264. function:
  8265. int Easter(int Year)
  8266. {
  8267. int Y, A, B, C, D, Month, Day;
  8268.  
  8269. /* Return false on domain error */
  8270. if ( (Year<1900) (Year>2099) ) return 0;
  8271.  
  8272. /* Compute Easter day */
  8273. Y = Year - 1900;
  8274.  
  8275. A = (7 * (Y % 19) +1) / 19;
  8276. B = (11 * (Y % 19) +4 -A) % 29;
  8277. C = ( Y + Y / 4 +31 -B) % 7;
  8278. D = 25 -B -C;
  8279.  
  8280. if (D>0) { Month = 4; Day = D; }
  8281. else { Month = 3; Day = D +31; }
  8282.  
  8283. /* Wrap and return result */
  8284. return Month + Day*10;
  8285. }
  8286. the result being easily unwrapped by the calling function without need of a
  8287. struct: Month = Result %10, Day = Result /10.
  8288. From all that I've read in recent months, it appears that a group of sturdy
  8289. civil calendar functions is something people need badly and keep on rewriting,
  8290. a bit like the many attempts to augmenting mantissa size in C numerical
  8291. formats. I have come across many algorithms of many kinds for calendar
  8292. calculations and now possess a modest but confusing collection of those, but
  8293. still try to improve on some of them. At this point, I took for myself some
  8294. obviously questionable directions, such as not to consider algorithms
  8295. requiring floating point, and sacrifice function domain for more basic data
  8296. types such as ints by choosing a suitable calendar origin.
  8297. From then on, some basic functions seem to be: day of the week, days between
  8298. dates, new date given a date and an offset in days, and full date string in
  8299. the country's language. (My compiler for sure has no strftime nor Locale.h.
  8300. There are a number of these algorithms, but which is fastest, smallest, most
  8301. accurate, simpler, most portable, easier to modify, etc.? We can't keep on
  8302. re-inventing the wheel, even if it's our own wheel. Could it be that someone
  8303. already did this? I don't want to spend a lifetime doing it, and bet most
  8304. people don't, but I offer my cooperation -- sorry it's no big thing.
  8305. Finally, just some tiny matters. I subscribed just very recently to CUJ, but
  8306. am finding myself asking things such as "Where was that function?" or "Where's
  8307. that paper on..." and browsing through my very lean collection of CUJ issues
  8308. (which I guard with my life, more or less.) Given that I also subscribe to the
  8309. disk listings, couldn't it be interesting to have in each one a index for the
  8310. issue, possibly with keywords, accessible to search with a grep or DOS FIND?
  8311. Authors usually know how to do that. I know it's a bore, but it doesn't take
  8312. too long either.
  8313. The final aspect is related with CUG Library. The most recent volumes have no
  8314. documentation readily accessible. For instance, without having ordered CUJ
  8315. back issues I would not have known that NEWMAT is a matrix/statistical package
  8316. (something I need very badly) but is unfortunately coded in C++ (quite an
  8317. icecold shower). I know you have such niceties as e-mail, but I don't (X.25 is
  8318. unnafordable) and it's not my fault, nor yours, obviously. It just happens
  8319. that I'm just an impoverished scientist.
  8320. Finally, allow me to congratulate you and the CUJ team on such an accomplished
  8321. journal. I am totally hooked on it and think I will subscribe to CUJ as long
  8322. as I code in C, which I expect will be for quite a while.
  8323. Best regards,
  8324. Joao C. de Magalhaes
  8325. R. Almeida Garrett 16 5E
  8326. 2795 CARNAXIDE
  8327. PORTUGAL
  8328. And I thought I knew everything about calendar computations. I'm just
  8329. returning from a visit to the CUJ intergalactic headquarters in Lawrence,
  8330. Kansas. Interestingly enough, we identified two supplemental services that we
  8331. felt should be beefed up. One is to make more available a machine-readable
  8332. index to past articles in CUJ. The other is to better educate our readers
  8333. about that little gold mine called the CUG Library. Your letter provides
  8334. useful reinforcement at a critical time. -- pjp
  8335. Dear CUJ,
  8336. I am addressing this question to you because I know of no one else who can
  8337. help me with this question. I have a DOS version of the editor vi. It is
  8338. called VIPC although it comes up as PC/VI when it loads up. -- This software I
  8339. believe was developed by:
  8340. Custom Software Systems
  8341. P.O. Box 678
  8342. Natick, MA 01760
  8343. 617-653-2555
  8344. 508-653-2555
  8345. I have tried contacting them but have been told by the Better Business Bureau
  8346. of Massachusetts that CSS is no longer in business or may be listed under a
  8347. new name. The reason I am trying to contact them is because I am trying to get
  8348. an updated version, as the version I have is about six years old. If they are
  8349. out of business, I was wondering how I could get hold of the source code to
  8350. this software. I realize that at one time the AZTEC C Compiler came with a
  8351. version of vi but I prefer the CSS version, as it is more like the Unix
  8352. version.
  8353. This is the specifics of the program:
  8354. name size date time
  8355. VIPC.EXE 95595 06-04-87 8:57p
  8356. The following is the text that is displayed as it loads up:
  8357. PC/VI Version 2.01 (IBM-PC) -- 6/04/1987
  8358. --Copyright (C) 1985-1987
  8359. Custom Software Systems
  8360. So if you know how I can get a new version of VIPC or the actual source code I
  8361. would greatly appreciate it.
  8362. Thanks,
  8363. Manuel Lopez
  8364. 6820 LBJ Frwy
  8365. Dallas, TX 75240
  8366. Anybody? -- pjp
  8367. Dear C Users Journal,
  8368. Just a note to Hutchinson Persons, Engineer, who so eloquently presented his
  8369. anthrocentric agenda in his letter published in your July 1993 issue. He
  8370. objected to Christopher Skelly's "errors of distinction" in his
  8371. personification of some computer terms and characteristics.
  8372. Mr. Persons, it warms me truly to see a man so intent on controlling his
  8373. environment. Damn the mosses! Let the human reign! With people like you at the
  8374. helm, the planet shall be truly lacquered. Your sense of irony seduces me.
  8375. What better way to point out the "imprecise thinking" in Mr. Skelly's
  8376. animative statements than to present your own? ("Can your p[n] make this
  8377. claim?")
  8378. Thank you, also, for pointing out "the importance of a human centered
  8379. philosophy." After all, when you state that "the word 'sense' applies to a
  8380. human ability", we human-centered philosophers, at least, are aware that dogs
  8381. do not really smell, bats do not really hear, and a whale feels nothing when a
  8382. half-ton fetus slips from its loins. Senses? Of course not, those are merely
  8383. the automatonous snappings of substandard synapses.
  8384. Finally, thank you for denying the evolution and creative use of language.
  8385. After all, if p[n] cannot "live" below p, then neither, as you sarcastically
  8386. point out, does fly-tying require "surgical ability." You back this up with
  8387. your other sarcastic statement that you "compute for a living." We "precise
  8388. thinkers," of course, know that you really mean "I write programs which enable
  8389. computers to compute, for a living." I salute you!
  8390. I hope that when we all move into sealed and sanitized geodesic domes, as a
  8391. result of our using "computers, electronics, chemistry, machinery, and any
  8392. other method (we) can...to enjoy...control...of (our) environment," that you
  8393. will be my neighbor, so that we, as humans can stand together and never be
  8394. "relegated to the status of the animal."
  8395. Sincerely,
  8396. Ed Hawco
  8397. Writer, Technical
  8398. 4854 rue Dagenais
  8399. Montreal, Quebec H4C 1L7
  8400. And I thought that I was hard on the guy.-- pjp
  8401. Dear Mr. Plauger,
  8402. It was interesting reading Anthony Naggs letter in the March 93 issue of The C
  8403. Users Journal. In this he was talking about trying to make the bubble sort
  8404. more useful. I have often wondered why anyone would bother with bubble sort
  8405. when, with the addition of a couple more lines of code, you can have a Shell
  8406. sort which is considerably faster. I have found the following version of Shell
  8407. sort to be very efficient and, as you can see, very easy to implement:
  8408. void shell_sort(int list[], int listSize)
  8409.  
  8410. {
  8411. int gap=listSize/2, goforward,
  8412. goback, temp;
  8413.  
  8414. while (gap > 0) {
  8415. for (goforward=gap;
  8416. goforward<listSize;
  8417. goforward++) {
  8418. goback = goforward;
  8419. while (list[goback--gap]
  8420. > list[goback]) {
  8421. temp = list[goback];
  8422. list[goback]
  8423. = list[goback-gap];
  8424. list[goback-gap]
  8425. = temp;
  8426. if ((goback -= gap)
  8427. < gap) break;
  8428. }
  8429. }
  8430. gap = gap * 3 / 5;
  8431. }
  8432. }
  8433. This beats the sock off bubble sort. For arrays of one million integers I've
  8434. found it to take about twice as long a quick sort. On the other hand, it does
  8435. have the advantage of tight memory control.
  8436. Regards,
  8437. Gordon Lingard
  8438. P.O. Box 1550
  8439. Armidale NSW 2350
  8440. Australia
  8441. glingard@neumann.une.edu.au
  8442. Your point is well taken. And for a rather small number of items, a bubble
  8443. sort can be smaller and faster than either Shell sort or quick sort. -- pjp
  8444. Dear Dr. Plauger,
  8445. I am a relatively recent subscriber to The C Users Journal. So far I am
  8446. finding it quite informative and an inexpensive way to improve my C and C++
  8447. skills. I have also purchased and read your book, The Standard C Library which
  8448. I also found interesting and useful.
  8449. In the process of attempting to design a string class for an application a
  8450. friend and I are working on, I studied several examples found in various books
  8451. I had purchased on C++. A problem that existed in all of them was an elegant
  8452. and simple way to handle exceptions to allocating memory. One implementation
  8453. never verified the return value of new at all -- which ran against my training
  8454. and experience. My initial solution was to use set_new_handler, but on
  8455. investigating this avenue further, it didn't seem to be ideal. One text
  8456. referred to set_new_handler as an interim solution and I didn't want to
  8457. consciously code something that is obsolescent.
  8458. Anyway, over the course of about a week of experimentation and study I finally
  8459. hit upon the following solution: overload the global new operator so that it
  8460. takes a function pointer argument:
  8461. void *operator new(size_t size, void
  8462. (*newException)());
  8463. This function pointer would be used to point to the desired exception
  8464. function. The code for the overloaded operator new is:
  8465. void *operator new(size_t size,
  8466. void
  8467. (*newException)())
  8468. {
  8469. Boolean quit = FALSE;
  8470.  
  8471. // allocate memory
  8472. void *p = malloc(size);
  8473.  
  8474. // if error allocating memory
  8475. if(!p)
  8476. {
  8477. // if newException points
  8478. // to a routine
  8479. if(newException)
  8480. {
  8481. // call the exception
  8482. // handler
  8483. newException();
  8484.  
  8485. // attempt to allocate
  8486.  
  8487. // memory once more if
  8488. // newException returns
  8489. if(NULL == (p =
  8490. malloc(size)))
  8491. quit = TRUE;
  8492. }
  8493.  
  8494. // exit if no handler defined
  8495. else
  8496. quit = TRUE;
  8497. }
  8498.  
  8499. // if memory allocation failed
  8500. if(quit)
  8501. {
  8502. cerr << "\nInsufficient memory.
  8503. Exited program...";
  8504. exit(EXIT_FAILURE);
  8505. }
  8506.  
  8507. // memory allocation succeeded,
  8508. // retum pointer
  8509. return p;
  8510. }
  8511. My intent was to have a general purpose memory allocation error function that
  8512. is called automatically. If the error routine returned, there would be one
  8513. more attempt to allocate memory before exiting the program.
  8514. I think I have accomplished that. In addition, some experiments with my own
  8515. code has convinced me that this approach eliminates a large amount of code,
  8516. which is to say, the object files are a lot smaller for modules that make
  8517. extensive use of dynamic allocation. While I haven't run the overloaded new
  8518. operator through a profiler, it seems reasonable to me that the reduction in
  8519. speed is not that significant. The standard global new operator is not
  8520. shadowed and is readily available should it be desired.
  8521. Later, it occurred to me that the same approach could be utilized with the
  8522. Standard C function malloc. It would be relatively simple to define a function
  8523. such as the following:
  8524. void *mymalloc(size_t size,
  8525. void (*exception)());
  8526. with code similar to that found above. This would greatly simplify writing
  8527. dynamic allocation routines since most applications will want to handle
  8528. exceptions in only a few standard ways. Using this method seems both simple
  8529. and elegant. Code would be easier to read, and executable files would be
  8530. significantly smaller (at only a slight cost in run time).
  8531. What do you think of this approach? Is it a good method or is there a
  8532. complication I'm not considering?
  8533. I can't help feeling a lot of people smarter than I have worked many years in
  8534. these languages. This has to have been considered at one time or another and
  8535. yet I have never run across it before.
  8536. Finally, what is the future of set_new_handler? Is it really intended to be an
  8537. interim solution or do the C++ committees intend to retain it?
  8538. Sincerely,
  8539. Randel Dale Astle
  8540. The joint committee definitely plans to retain the function set_new_handler,
  8541. with just a few refinements in its semantics. As you have observed,
  8542. programmers do not always check whether a new expression succeeds. Thus, the
  8543. joint committee has introduced an exception that is thrown by default when the
  8544. expression fails. Reconciling this behavior with past practice involves a few
  8545. subtleties that I'd rather not explore here.
  8546. I like your approach of passing function pointers for exception handlers. Yes,
  8547. I've seen it before, in one form or the other, but it's not widely used. My
  8548. guess is that most programmers don't want to have to specify the handler
  8549. function on each call. -- pjp
  8550. Dear Sir:
  8551. I recently subscribed to the CUJ, and enjoy it very much. I especially enjoyed
  8552. the articles on curve fitting, the alpha/beta filter, and recovering distorted
  8553. wave forms. I would very much like to see some good, fully researched articles
  8554. on the following topics:
  8555. Fast Fourier Analysis
  8556. Maximum Entropy Spectral Analysis
  8557. Maybe one of the excellent "engineer" authors would do this for CUJ.
  8558. I am not an engineer, but I need to use these methods in a project that I am
  8559. working on. I have not been able to find anything concerning these topics in
  8560. any of the programming magazines. Any help or comments will be appreciated.
  8561. Thank you.
  8562. Sincerely,
  8563. Delbert Bourling
  8564. 648 Maple Grove Rd.
  8565. London, KY 40741
  8566. P.S. Any published information on "Maximum Entropy Spectral Analysis" seems to
  8567. be very, very scarce. I think some good algorithms and C/C++ code would be
  8568. very useful to a wide range of CUJ readers.
  8569. Your interest is noted. Potential submittors might note the same.-- pjp
  8570. Howdy, howdy!
  8571. Not only am I going to try out your journal but I am going to present you with
  8572. a challenge to see how useful you can be to me.
  8573. In mode 18 (native VGA) I draw X-Y axes on a screen, label them, make tick
  8574. marks on the axes, and again label them. Then I draw a graph from data stored
  8575. in an array. But now I want to send a duplicate of this to a printer and I
  8576. don't really want to wait more than a minute for this to happen. Presently, I
  8577. can redraw the image on a hidden video page and do a kind of screen dump by
  8578. reading the entire screen pixel by pixel, row by row, and loading it into an
  8579. array. Then I take a third-party printing utility (PGL Toolkit) to send the
  8580. array to the printer. It is slow and the output is poor. If I had their source
  8581. code, I would see if I could redesign it for my particular application and
  8582. recompile it as a subroutine in my program. Also their printer drivers could
  8583. stand some improvement. How do Microsoft and WordPerfect get their graphics
  8584. images to a printer? I would like to know how and do it myself. If you have
  8585. some alternatives or know something I don't, please send me some info.
  8586. Hoping this finds you willing and able I am
  8587. Jim Baugh
  8588. 412 South Wakefield Drive
  8589. Lafayette, LA 70503-4632
  8590. Anyone want to rise to the challenge? -- pjp
  8591. P.J. Plauger,
  8592. Recently I was made aware of the existence of The C Users Journal. What do I
  8593. have to do to subscribe? I would also be interested in contributing. Your
  8594. monthly column is something I always enjoy.
  8595.  
  8596. I have been a practicing programmer for about 30 years and your comment about
  8597. "computer science" in the June 1993 Embedded Systems Programming struck a
  8598. chord. I have never seen any justification for either "computer science" or
  8599. "software engineering." My view has always been that these are just
  8600. power-grabbing ploys by the two established disciplines of Science and
  8601. Engineering, both of whom saw it as a threat to their cozy worlds and a chance
  8602. to grab some of the kudos and dollars that would go along with annexing this
  8603. new and useful but essentially unrelated activity.
  8604. Programming has always seems to me to be a skilled craft. It has also been my
  8605. observation that some people can do it and some can't. The ones who can't just
  8606. never seem to get it, and no amount of training, etc. will help. I learnt to
  8607. program in the early 1960s without the benefit of formal teaching by anybody
  8608. who knew anything at all, simply because there weren't many such people
  8609. available. Over the years I have managed to correct most (but not all) of the
  8610. bad habits acquired and the process has left me with a keen awareness that I
  8611. should always be looking at how I do things and be ready to change when there
  8612. is obviously a better way.
  8613. Which brings me to other thing which you have mentioned and causes me some
  8614. perplexity at the moment, C++. As a practitioner, I have embraced most of the
  8615. other significant developments in programming that have occurred over that
  8616. last three decades. In just about every case they solved an obvious problem or
  8617. pointed out a better way to do things. I can't get excited too much about C++.
  8618. Maybe I am just getting too old for this and should go find something else to
  8619. do, but comparing C++ to the elegant simplicity of Pascal or Modula leaves me
  8620. cold. The fact that the authors/creators in many instances have simply
  8621. replaced one set of jargon with another doesn't help in sorting out what is
  8622. substantive in what is being offered.
  8623. I guess I will have to go with the flow and at least try one project with C++.
  8624. Frank Campbell
  8625. uunet!mti.com!campbell
  8626. I think you're being a bit hard on computer science and software engineering.
  8627. There are definitely both scientific and engineering principles that are
  8628. highly relevant to the design and use of computers. I agree that programming
  8629. is a craft, having learned it much as you have, but many a craft has been
  8630. improved by the application of discipline and technology.
  8631. As for C++, it is certainly not elegant in the same sense as Pascal, Modula,
  8632. or even good old C. I find that C++ comes into its own is with larger
  8633. programs. If you have little occasion to work on large projects (where "large"
  8634. is admittedly a relative term), you may find few compelling reasons to use
  8635. C++. -- pjp
  8636. Dear Mr. Plauger:
  8637. I have a number of questions regarding file handling functions that I hope you
  8638. can answer, or else direct me to a publication which discusses this area.
  8639. The Microsoft C60.0 library includes three groups of file handling function;
  8640. fopen, open, and dos open. Other than the buffering offered by the fopen
  8641. family, I do not know what their other advantages and disadvantages are. For
  8642. example, is there any difference between using _dos_read to read file data to
  8643. a far buffer, and using read compiled with the large memory model?
  8644. A second question relates to DOS system buffers. When a C function writes data
  8645. to the hard disk, does it initially write to a DOS buffer, and then to disk
  8646. when the operating system decides? I presume the system buffers are the ones
  8647. defined in the CONFIG.SYS file. I also presume the buffering provided by the
  8648. fopen family uses different buffers. Is there any way to ensure that data is
  8649. immediately written to the disk?
  8650. Finally, how does SHARE work? I use it with the fopen and dos_open functions
  8651. because my programs are often used on networks and I need to implement record
  8652. and file locking. I am confused about SH_DENYNO and SH_COMPAT. For example,
  8653. Microsoft recommends using SH_COMPAT for DOS-based networks, but I have only
  8654. been able to implement proper file sharing on Lantastic and Novelle networks
  8655. using SH_DENYNO -- SH_COMPAT gives a "sharing violation" whenever a second
  8656. program attempts to open a file already open but not locked.
  8657. I would be very thankful for any light you can shed on these problems. Thank
  8658. you.
  8659. Yours truly,
  8660. R.W.J. Ford, M.D.,
  8661. Department of Anaesthesia
  8662. Shaughnessy Hospital
  8663. 4500 Oak Street, Room A437A
  8664. Vancouver, B.C. V6H 3N1
  8665. I can only give you partial answers, since I tend to stick to fairly portable
  8666. C. Function open and its brethren model the original UNIX style of I/O. When C
  8667. started getting moved among machines, fopen and company were added. These
  8668. guarantee a reasonable amount of buffering, and insulation from peculiarities
  8669. of the underlying operating system, at the cost of still more machinery and
  8670. fewer supporting operations. The DOS versions give you access to more
  8671. DOS-specific features. In particular, you can choose whether or not to lose
  8672. those carriage returns that DOS has and UNIX lacks when you read from a file.
  8673. You can pretty well count on system buffers to be involved in practically all
  8674. reads and writes. The buffering logic within DOS has to deal with blocking and
  8675. unblocking sectors of various sizes, and with various limitations on how well
  8676. DMA channels can address different parts of storage.
  8677. All I know about SHARE is what you apparently know -- that it helps minimize
  8678. collisions among processes reading and writing the same files in parallel. I
  8679. know it is supposed to support more sophisticated kinds of sharing such as you
  8680. describe, but I've never had occasion to program with SHARE in mind. -- pjp
  8681. Dear Mr. Plauger,
  8682. My apologies for writing to you at a rival publication, but this was the most
  8683. obvious address to get you at. The purpose of the letter is to tell you how
  8684. much I have enjoyed "Programming On Purpose" over the years, and how much I
  8685. will miss it years to come.
  8686. I found your column to be one of the most thought provoking I have ever read
  8687. (and not just from a programming point of view) -- and often used to bring the
  8688. themes up in conversation at my place of work to get a debate going around the
  8689. issues you raised. I cannot say I always agreed with you. I never, however,
  8690. disagreed strongly enough to write a letter to the author!
  8691. I sincerely hope the book Programming On Purpose: Essays on Software Design,
  8692. as well as its companions, is released in South Africa. Just in case, would
  8693. you mind sending me the ISBN numbers of the books at the above address. This
  8694. will help me to get hold of them easily. I believe the publisher is
  8695. Prentice-Hall.
  8696. Once again, thank you for some provocative stuff.
  8697. Yours Sincerely,
  8698. John Bannister
  8699. P.O. Box 32092
  8700. Braamfontein, South Africa 2107
  8701. Volume 1 (design) is ISBN 0-13-721374-3, volume 2 (people) is ISBN
  8702. 0-13-328105-1, and volume 3 (technology) is ISBN 0-13-328113-2. All are indeed
  8703. published by Prentice Hall and all originally appeared in Computer Language.
  8704. -- pjp
  8705. Dear Dr. Plauger,
  8706. Thank you for printing the letter in which I asked Mr. Pugh why my int10h
  8707. handler successfully controlled printf scrolling on monochrome, while EGA/VGA
  8708. systems appeared to scroll without calling int10h at all (CUJ March '93,
  8709. p.124).
  8710. If you'll permit it, I'd like express publicly my thanks to Brian Knoblauch
  8711. (Toledo, OH), Ir. H. Hahn (Veldhoven, The Netherlands), Steve Ferrell (Duluth,
  8712. MN), and Carl Smotricz (Hattersheim, Germany). They took the time to share
  8713. their experience and knowledge about EGA/VGA scrolling with me. Mr. Knoblauch
  8714. suggested that I investigate int42h. Mr, Ferrell recommended PC Interrupts
  8715. (Ralf Brown and Jim Kyle, Addison-Wesley, 1991), and said int42h was used as a
  8716. replacement for int10h on some EGA/VGA systems. Mr. Hahn gave details about
  8717. intercepting inth10h AH=13h (Display String) to control scrolling from string
  8718. display functions. Mr. Smotricz had discovered that some systems call int10h
  8719. AH=6 (Scroll Up) from int10h AH=9 (Write Char & Attr) and int10h (Write Char);
  8720. he suggested that I intercept those functions in addition to intercepting
  8721. Scroll Up.
  8722. Disassembling int42h on an AST Premium 386 showed that it was indeed an int10h
  8723. workalike, as Mr. Ferrell and Mr. Knoblauch indicated. In particular, the
  8724. Scroll Up function (AH=6) looked like that of int10h. But when I modified my
  8725. program to intercept int42h AH=6 in addition to int10h AH=6, nothing changed:
  8726. Scrolling was controlled as expected on the XT-clone monochrome system, but
  8727. not on the AST VGA or a 386SX VGA with Award BIOS. Nevertheless, the
  8728. suggestions provided by Mr. Ferrell and Mr. Knoblauch provided valuable
  8729. experience which I will use in future projects.
  8730. My experiments showed that intercepting String Display (AH=13h) or Write Char
  8731. (AH= 9 and 10h) of int10h would make screen updates too slow for my current
  8732. application. Still, the detailed information on these points given by Mr.
  8733. Smotricz and Mr. Hahn will be useful in other contexts. I'm glad they didn't
  8734. let either national boundaries or the Atlantic Ocean stop them from giving me
  8735. the benefit of their hardwon experience.
  8736. Since there doesn't seem to be a good way to make my int10h method control
  8737. printf scrolling on EGA and VGA, I had to look at alternatives. The solution I
  8738. finally implemented was a modification of a method I had devised prior to the
  8739. int10h method. It involves changing DOS Offset of Video Buffer (OVB) value
  8740. stored at absolute address 0:44E to point to the first colunm of top row to be
  8741. scrolled, and clearing 25 rows of video-buffer memory at that offset. This
  8742. "OVB method" assumes that video page 0 is current, and that the last byte of
  8743. page 0 is immediately followed by at least 3,840 bytes of unused memory (which
  8744. is usually the first 24 rows of video page 1). The original version of my OVB
  8745. method worked on a Hercules-clone monochrome system, and on a Paradise VGA
  8746. Professional, but failed to control scrolling on an IGC 20 TIGA system, and on
  8747. one other machine with an unknown video system. That's why I abandoned it in
  8748. favor of the int10h method outlined in my earlier letter (CUJ 93 March,
  8749. p.124).
  8750. The failing systems behaved as if they reset the OVB word at 0:44E to 0
  8751. repeatedly. (Indeed, the BIOS listings in the IBM Hardware Technical Reference
  8752. (84apr) reveal that a call to int10h AH=0 (Set Mode) or 5 (Set Active Page)
  8753. will cause the word at 0:44E to be reset to 0.) In the original version of my
  8754. OVB method, I set the scroll-limiting OVB value only once, during program
  8755. initialization. To make it work in spite of repeated resetting, I wrote a
  8756. function to use instead of printf whenever limited scrolling was in effect.
  8757. The replacement function (Listing 7) re-assigns the scroll-limiting value to
  8758. the OVB word at 0:44E prior to each screen write.
  8759. [The listings mentioned in the preceding and subsequent paragraphs are omitted
  8760. because of their length. We include them on the monthly code disk. -- pjp]
  8761. This solution controlled scrolling on all systems tested (Hercules, Paradise
  8762. VGA Pro, and TIGA), but a problem remained: Lines written in the scrolling
  8763. rows did not appear at all on the TIGA screen, yet displayed as expected on
  8764. the other systems.
  8765. To investigate the TIGA problem, I wrote a small test program so I could
  8766. experiment with the effect of modifying three additional video parameters in
  8767. various combinations. (The OVB word at 0:44E was changed for every test). I
  8768. found that modifying the Start Address Register didn't work on any of the
  8769. three test machines. Modifying the Screen Length word at 0:44C was not
  8770. effective on any of them, either.
  8771. In the test program, changing the Number of Displayed Rows at 0:484 to
  8772. correspond to the new value at 0:44E worked on all the test machines. For
  8773. example, if row 0 through row lockrw were to remain locked on the screen while
  8774. rows lockrw+1 through 24 were allowed to scroll up, putting the value
  8775. 160*(lockrw+1) at 0:44E and 23-lockrw at 0:484 would work on all test
  8776. machines.
  8777. Unfortunately, when I tried this method in a production program, the TIGA
  8778. machine showed display anamolies. At that point, I decided that it would not
  8779. be economically feasible to continue trying to include TIGA systems in this
  8780. version of the production program. I removed the code for changing the Number
  8781. of Displayed Rows value at 0:484 from the production program; and it worked as
  8782. expected on all test machines except the TIGA.
  8783. To use the OVB method, you'll need code similar to that shown in LOCKVROW
  8784. (Listing 2) to lock a specified video row on the screen while allowing those
  8785. below it to scroll. You'll also need something like UNLOCKVR (Listing 3), so
  8786. you can restore normal scrolling before returning control to the operating
  8787. system, or before each section of code that needs a visible cursor.
  8788. The cursor should be hidden (HIDECURS, Listing 5) during locked-row operation,
  8789. because its position is relative to the changed offset at 0:44E, while the
  8790. visible characters on the screen are always relative to offset 0. For example,
  8791. if the cursor is visible on row 24 when 0:44E is 0, changing 0:44E to 2720 (to
  8792. lock row 17 on the screen) will put the cursor on row 24 of the "new" screen
  8793. that begins at offset 2720 of the video buffer. But that row isn't visible on
  8794. the displayed screen that begins at offset 0 (it would be row 41, counting
  8795. from there).
  8796. Furthermore, experience shows that printf (and scanf) ignore the 0:44E offset.
  8797. Their data is relative to offset 0 of the video buffer, regardless of 0:44E.
  8798. In one experiment on a Hercules clone, I set 0:44E to 17*160=2720, then used a
  8799. BIOS call to put the cursor on row 0. The cursor immediately appeared on row
  8800. 17, but both printf and scanf operated as if the cursor were on row 0:
  8801. characters appeared on row 0 as the cursor moved in step along the columns of
  8802. row 17.
  8803. For sections of code that need a visible cursor, as when requesting and
  8804. obtaining keyboard input, UNLOCKVR (Listing 3) can be used to restore the OVB
  8805. word at 0:44E, and NORMCURS (Listing 6) will restore the cursor. To return to
  8806. locked-row operation, use HIDECURS (Listing 5), then use RELOCKVR (Listing 4).
  8807. I hope others can benefit from our combined experience with controlling printf
  8808. scrolling. Thanks again to you and to the other four people who so unselfishly
  8809. helped me. It's encouraging to see free exchange of ideas continuing in spite
  8810. of software patenters who, ignoring that all ideas are derived from others --
  8811. which are based on the ideas of still others, ad infinitum -- conceitedly
  8812. claim exclusive ownership of any they might use in software. Please continue
  8813. your valuable contributions to the international community of programmers,
  8814. students, and teachers.
  8815. Sincerely,
  8816. Sid Sanders
  8817. 5 Seneca Avenue
  8818. Geneseo, NY 14454-9508
  8819. I add my thanks to all our readers who have repeatedly demonstrated a
  8820. willingness to share their knowledge with others. -- pjp
  8821. To the editor:
  8822. Mr. Ralph Franke has called to my attention an error in the RPFT code
  8823. described in "Curve Fitting With Extrapolation, C Users Journal, June 1993.
  8824. The statement for reading the command-line parameter -DIG in line 47 of
  8825. function commd reads only the second digit. Lines 46 and 47 are:
  8826. if (!strncmp(&argv[i][0],"-DIG=",5))
  8827. { k=sscanf(&argv[i][6],"%d",dig);
  8828.  
  8829. but should be:
  8830. if (!strncmp(&argv[i][0],"-DIG=",5))
  8831. { k=sscanf(&argv[i][5],"%d",dig);
  8832. Mr. Franke also pointed out that, for data which are an exact fit to a
  8833. polynomial and which include a Y value of zero which is not the first or last
  8834. datum, the interpolation routine may give incorrect results with no warning
  8835. message. This is not a problem for inexact empirical data, but the revised
  8836. routine shown in Listing 8 should be used for safety.
  8837. This version can still fail for some abscissae with exact polynomials such as:
  8838. y = 10 x -9 x^2 + 8 x^3 -7 x^4 + 6x^5
  8839. For x = -3, -2, -1, . . . , 12
  8840. That function is fit properly by RPFT, but in a separate test using the
  8841. interpolation routine by itself it failed at some points such as X = -1.5 with
  8842. the message that a pole (zero denominator in the interpolating function) may
  8843. exist at that point. X = -1.500001 runs OK.
  8844. Lowell Smith
  8845. 73377.501@compuserve
  8846. We provide the corrected version of RPFT on the monthly code disk. -- pjp
  8847. Dear CUJ,
  8848. First, let me say that it would be nice if prospective letter writers were
  8849. directed to an e-mail address. I didn't really want to dump on you, Mr.
  8850. Plauger. :-) [Use cujed@rdpub.com -- mb]
  8851. I enjoyed Chuck Allison's July article on C++ Streams. However I wish somebody
  8852. would provide an exhaustive and accurate list of the effects of all format
  8853. flags for both input and output. Also, there is a lot of confusion out here
  8854. regarding the duration of these flags and items set by manipulators such as
  8855. width, precision, and fill.
  8856. I have been through talks or books by Stevens, Stroustrup, Coplien, Saks,
  8857. Semaphore's Shewchuk, Rowe, Swan, and Allison, and I still am not sure on a
  8858. couple of points. Specific areas of confusion include:
  8859. Q. Is the default for ios::basefield all bits off? Is this the same as
  8860. ios::dec? For Borland, at least, the default for an istream is all bits off,
  8861. and this is not the same as having ios::dec set. For instance, given the
  8862. example in Listing 9, try typing "0x12" three times:
  8863. 0x12 0x12 0x12
  8864. 18 cin.good()=1
  8865. 18 cin.good()=1
  8866. 0 cin.good()=1
  8867. 0 cin.good()=0
  8868. 0 cin.good()=0
  8869. The first time uses the default input setting, and converts to hex. The second
  8870. is explicitly set to 0 (the default) and also converts to hex. The third input
  8871. explicitly sets the decimal flag and stops reading at the "x" giving you a
  8872. value of zero. The other inputs choke on the "x."
  8873. If you think this is esoteric, have a user enter "010" in a field where you
  8874. have not set the base explicitly to ios::dec! You guessed it, you get a
  8875. conversion to octal for a value of 8.
  8876. Q. How long are width, precision, and fill in effect for istreams? ostreams?
  8877. Best Regards,
  8878. Dave Rogers
  8879. Frank Russell Company
  8880. 76366.2171@compuserve.com
  8881. The Library Working Group of the joint C++ standards committee is indeed
  8882. working to clarify the issues you raise. Currently, they suffer from an excess
  8883. of variety, as you point out. Much of the basic work in this area has already
  8884. been done for the LWG by Jerry Schwarz, the original author of iostreams. I
  8885. will be answering questions such as this in more detail in my column,
  8886. "Standard C" (admittedly a slight misnomer here), and later in the book I am
  8887. currently writing on the Standard C++ library.
  8888. For now, I will simply say that ios::basefield should be initialized to
  8889. ios::dec. Precision and fill stay in effect until you explicitly change them.
  8890. Width gets set to zero by conversions that use the width. -- pjp
  8891. Dear C User's Journal:
  8892. I enjoyed the article on "Automated Unit Testing" by Roger Meadows in the
  8893. August 1993 CUJ (pp. 53-58). I did notice a bug that the test routine did not
  8894. catch in the strws function. If the input string ends in whitespace,
  8895. processing continues to run through memory until it finds a \0 terminator that
  8896. is not preceeded by whitespace.
  8897. I realize that the point of this article was to point out how to include a
  8898. main routine for testing purposes but it should also be noted that this is
  8899. just the type of error that is very difficult to track down when you get "bus
  8900. error -- core dumped" much later due to other data being walked on. Especially
  8901. when it is dependent on a specific data pattern that will likely be very
  8902. intermittent and hard to reproduce.
  8903. Anyone writing testing routines needs to look very carefully at potential
  8904. errors in the routine being tested. In my opinion, any syntax like "*to++ =...
  8905. ", should raise a flag that says "you'd better make sure you don't
  8906. accidentally walk out of the bounds you intend." This means adding at least a
  8907. suffix to the data. In this case, the suffix must contain multiple blanks or
  8908. tabs to detect the problem.
  8909. An example is shown in Listing 10.
  8910. Consider the string "test\t", after copying the "test" characters, to and from
  8911. both point to the tab. A blank is copied instead of the tab and both pointers
  8912. are advanced to point to the \0 terminator. The inner while is not executed
  8913. since we are pointing to the end of the string. We now come out and copy the
  8914. next character since it is not a blank or tab. However, it happens to be the
  8915. \0 terminator. The pointers are advanced and no longer point to the
  8916. terminator. The outer while will continue to process until it finds a
  8917. terminator not preceded by a blank or tab.
  8918. The fix is to add a condition, as shown in Listing 11.
  8919. And to include in the test code as shown in Listing 12.
  8920. DISCLAIMER: I have not actually tested this code, these are just my thoughts
  8921. while reading the article. You should check them before publishing any
  8922. comments including this code.
  8923. Ed Sarlls, III
  8924. Western Geophysical Exploration Products
  8925. Houston, TX, USA
  8926. sarlls@wg2.waii.com
  8927. Opinions expressed are not Western Geo's and may not even be mine.
  8928. Roger Meadows replies:
  8929. Thank you for your comments on the article and for finding the problem with
  8930. the sample program. I followed the process presented in the article to fix the
  8931. problem. First, I modified the test portion of the sample program so that it
  8932. finds the bug you described. I used the test code modifications you suggested.
  8933. The changes did cause some of the test cases to fail. However, it seemed that
  8934. all of the test cases should have failed. I had to increase the length of the
  8935. test suffix to get all of the test cases to fail. Then, I modified the
  8936. application code, also using the modifications you suggested, to fix the
  8937. problem. Rerunning the test code demonstrated that the fix worked and that it
  8938. did not break anything.
  8939. I think your suggested modification to the test code makes a good addition to
  8940. the rules for writing automated test code.
  8941. 8. Make sure application code does not write beyond the end of buffers.
  8942. STRWS. C, a revised listing of the sample program with "/*new*/" at the end of
  8943. new lines, is available on the monthly code disk.
  8944. Dear Mr. Plauger,
  8945. I have been reading CUJ now for several years. My work has benefited much from
  8946. the feature articles and editorials in your magazine. Now I would like to
  8947. query that immense knowledge base for a specific need. I am involved in the
  8948. development of a large data acquisition system running the iRMX operating
  8949. system. The system is distributed around an FDDI network that is implemented
  8950. primarily with virtual circuit connections linking software modules. We need
  8951. to synchronize the time on all of the machines as closely as possible. I was
  8952. wondering (hoping) that either you or one of the readers knows of some
  8953. iterative-feedback type algorithm for performing such a synchronization; sort
  8954. of a software version of the four-wire power supply.
  8955. Thank you,
  8956. Frank Metayer
  8957. Electric Boat
  8958. Groton, CT 06340
  8959. I don't, but I hope one of our readers does. -- pjp
  8960. Dear Mr. Plauger,
  8961. I was just sitting here reading the letters to the editor in Vol. 11 No. 7 of
  8962. CUJ, waiting on my compiler to decide if I got it right. In reading the
  8963. letters, I'm amazed at how critical they are! I hope you have a similar number
  8964. of positive letters. Please don't be discouraged. I believe you provide an
  8965. excellent service and a valuable resource. Keep up the good work!
  8966.  
  8967. Tedd Gimber
  8968. Letters tend to be more negative than positive, I think because anger is a
  8969. more powerful motivator to action than mere joy. Mostly, I've learned to
  8970. mentally compensate for that bias. But I still enjoy letters like yours
  8971. whenever they come in. Thanks. -- pjp
  8972. Chuck Allison:
  8973. In your article in the August 1993 of The C User Journal, you talk about the
  8974. void * pointer. Well, is it true that void * == (int * or char * or float *)?
  8975. Why or why not? Please explain because I would like to know why the following
  8976. statement is invalid:
  8977. struct PIZZA {
  8978. int key;
  8979. /* Other fields. */
  8980. };
  8981.  
  8982. void main(void)
  8983. {
  8984. PIZZA *myPizza
  8985. = calloc(10, sizeof(PIZZA));
  8986. /* ERROR message generated. */
  8987.  
  8988. ...
  8989.  
  8990. }
  8991. Thanks in advance,
  8992. -Con(rad)
  8993. ps: I received an error message for the above statement for every compiler
  8994. that I tried to run my code on.
  8995. Chuck Allison replies:
  8996. If you are using C, the problems with your program are:
  8997. 1) You didn't include <stdlib.h> for calloc
  8998. 2) You didn't qualify PIZZA with struct (or typedef it).
  8999. The program shown in Listing 13 works fine.
  9000. If you are using C++, you really shouldn't be using calloc. Listing 14 shows a
  9001. C++ version that works.
  9002. As far as your question about void * -- it is an animal unto itself. It is not
  9003. an int * or any other *. Its purpose is to allow assignment to and from
  9004. pointers to any type. It cannot be dereferenced, hence it is impossible to
  9005. think of it as an int *. This has little to do with your program excerpt.
  9006. Since calloc returns a void *, it can be assigned (in C) to a PIZZA * or any
  9007. other pointer type. Correct problems 1) and 2) above, and you're home free.
  9008. Hi,
  9009. Just read your article in The C Users Journal. I'd like to propose something
  9010. that would be of great benefit to control progammers like I am. Embedded
  9011. systems, and dedicated controllers often need the equivalent of floating-point
  9012. fractional numbers, without the overhead of a floating-point package. I'd like
  9013. to see a modifier as follows:
  9014. fixed -- This modifier implies that the variable associated with is is
  9015. inherently split in the middle with a binary point. All math associated with
  9016. it takes this into consideration. for example:
  9017. fixed int scale_factor; -- would have (on a 16-bit machine) an 8-bit integer
  9018. portion and an 8-bit fractional portion, and thus could represent 0 to
  9019. 256.9960. By the same token, fixed unsigned char would have a 4-bit integer
  9020. portion (0-15) and a 4-bit fraction (0.9375). A fixed unsigned long would of
  9021. course have a 16-bit integer and a 16-bit fraction.
  9022. Conversion rules:
  9023. Fixed of one size to fixed of another: smaller to larger i.e. 4.4 -> 8.8 would
  9024. simply have the first 4 bits placed into bits 0-3 of the first byte, and the
  9025. second 4 bits placed into bits 7-4 of the second byte. For larger to smaller,
  9026. such as 8.8 -> 4.4, bits 0 to 3 of the first byte (in the 8.8) would be placed
  9027. into bits 7-3 of the 4.4 and bits 7-3 of the second byte of the 8.8 would go
  9028. into bits 3-0 of the 4.4
  9029. Conversion to integers would simply drop the fractional portion. Conversion to
  9030. floats would give the floating point equivalent. I have had to write these
  9031. sorts of things many times, and it is always an aggravation.
  9032. The second thing that I'd like to propose is a type called quad. quad is a to
  9033. a long as double is to a float. If a long is 32 bits, then a quad is 64 bits.
  9034. quads could also be prefaced with the fixed modifier.
  9035. Cheers,
  9036. Woody Baker
  9037. Postscript consultant/Flint knapper
  9038. Austin, TX
  9039. woody@knapper.cactus.org
  9040. A superset of the fixed-point arithmetic you describe is in PL/I and Ada. I
  9041. don't know how widely it actually gets used. I believe that the Numerical C
  9042. Extensions Group (X3J11.1) has also explored extended-precision integers. --
  9043. pjp
  9044. Dear Bill,
  9045. Thank you for the opportunity to present my article on "Extending C for Object
  9046. Oriented Programming" (though I fear it will mark me for life as the "Macro
  9047. King"). I have since received several kind letters by email asking for source
  9048. and reporting bugs. It's letters like these make all the hassle worthwhile. I
  9049. have also copied you on my mailing of the latest source in case you want to
  9050. update the code disk for the article.
  9051. Yours sincerely,
  9052. Greg Colvin
  9053. gregc@ihs.com
  9054. P.J. Plauger:
  9055. Salutations from the other side [of the world]. I've just finished reading
  9056. your April editorial in CUJ, and wish to agree wholeheartedly. In the last 12
  9057. months, it seems my professional world has been turned on both its ears with
  9058. release after release of software, each claimed to be an improvement over the
  9059. last. In general, this is true, but I can't help lying awake some nights
  9060. feeling completely inadequate in my abilities to keep up with the pace of
  9061. change. It makes me feel much better to see that the giants of this
  9062. programmer's world (for such you are, even if you don't see yourself that way)
  9063. also suffer some of the same feelings. While I have your ear, or eyes, is it
  9064. possible to order backissues of CUJ by email? It is a long way from Melbourne,
  9065. Australia, to basically anywhere, and it would be considerably more convenient
  9066. for me to order them by email rather than snailmail. Having only recently
  9067. subscribed to CUJ, I am still catching up (since we get magazines about three
  9068. months late down here), and I appear to have missed the March 93 issue. If you
  9069. have not already done so, do give Visual Basic a look. While not complete in
  9070. itself, it can be a lot of fun to use, and when combined with C, it becomes a
  9071. truly powerful environment. Enough of this. I thank you for your time in
  9072. reading this.
  9073. Stay sane.
  9074. -Craig
  9075. We are working on smoothing the process of ordering back issues electronically
  9076. and otherwise. Meanwhile, I've forwarded your letter to R&D's customer service
  9077. folk. -- pjp
  9078. Dear PJP:
  9079. I read your article "Developing the Standard C++ Library" in the October, 1993
  9080. issue of The C Users Journal. I am very anxiously looking forward to your book
  9081. on the Standard C++ library.
  9082. As a member of the C++ user community I would like to add my voice to the
  9083. outrage over the delay in getting some kind of documentation of the iostreams
  9084. interface to the public. To my way of thinking, even greater fervor should be
  9085. applied to releasing some minimal documentation. When the ANSI standard for C
  9086. was developed, K&R had already been published for several years and users were
  9087. comfortable with the stdio library interface. If any changes were made to
  9088. stdio as a result of the ANSI standard, it was relatively easy to make code
  9089. adjustments.
  9090. This is not the case with C++, since as you point out there is very little
  9091. documentation available on iostreams. Steve Teale's book is a step in the
  9092. right direction and for that he deserves praise. The title of the book is
  9093. somewhat misleading since the book does not pretend to be the definitive
  9094. iostreams reference. As a further criticism, the book references ANSI working
  9095. documents by Jerry Scharz and Mike Vilot which are not available to the
  9096. general public.
  9097. To the members of the ANSI committee who are working hard to get iostreams
  9098. standardized, I apologize. Those members who are prolonging the development of
  9099. the standard with excessive concern for minutiae should be set straight. There
  9100. is a desparate need now in the user community for iostreams documentation. The
  9101. ANSI committee should set an immediate goal of informing the public as to what
  9102. iostreams features and interfaces are likely to remain stable in the standard.
  9103. I look forward to your upcoming columns on the C++ standard library as well as
  9104. your book. I hope you, Steve Teale, or someone else will soon satisfy the need
  9105. for an iostreams reference.
  9106. Larry Johnson
  9107.  
  9108. NCR, Lisle
  9109. cuuxb!laj
  9110. 708-810-6524
  9111. (VP 473-6524)
  9112. I can only share your concern. And believe me, I want to get my C++ library
  9113. book out soon, too. -- pjp
  9114.  
  9115. Listing 8 ratint.c
  9116. Function RATINT} for the RPFT Code
  9117. /******************************************************
  9118. * RATINT - Diagonal rational function interpolation in
  9119. * the arrays xa[1..n] and ya[1..n].
  9120. *****************************************************/
  9121. void ratint(double xa[], double ya[], double *c,
  9122. double *d, int n, double x, double *y)
  9123. { int m,i,ns=1;
  9124. double w,t,hh,h,dd;
  9125. static double miny=1.e99;
  9126.  
  9127. if (miny>1.e90) for (i=1; i<=n; ++i)
  9128. if (ya[i]<miny) miny=ya[i];
  9129. hh=fabs(x-xa[1]);
  9130. for (i=1;i<=n;i++)
  9131. { h=fabs(x-xa[i]);
  9132. if (h == 0.0) {*y=ya[i]; return; }
  9133. else if (h < hh) { ns=i; hh=h; }
  9134. c[i]=ya[i]-miny; d[i]=ya[i]-miny+1.e-50;
  9135. }
  9136. *y=ya[ns--] - miny;
  9137. for (m=1;m<n;m++)
  9138. { for (i=1;i<=n-m;i++)
  9139. { w=c[i+1]-d[i] ; h=xa[i+m]-x; t=(xa[i]-x)*d[i]/h;
  9140. dd=t-c[i+1];
  9141. if (fabs(t)>1.e15)
  9142. fprintf(stderr,"Probable loss of accuracy in"
  9143. "RATINT. fabs(t) > 1.e15 for X = %.8G\n",x);
  9144. if (dd == 0.0)
  9145. { fprintf(stderr,"Error in routine ratint. The"
  9146. "function may have a pole at x=%.8G\n",x);
  9147. exit(1);
  9148. }
  9149. dd=w/dd; d[i]=c[i+1]*dd; c[i]=t*dd;
  9150. }
  9151. *y += (2*ns < (n-m) ? c[ns+l] : d[ns--]);
  9152. }
  9153. *y += miny; return;
  9154. }
  9155.  
  9156. /* End of File */
  9157.  
  9158.  
  9159. Listing 9 flagxmpl.cpp
  9160. #include <iostream.h>
  9161. #include <iomanip.h>
  9162. int main(int, char**)
  9163. {
  9164. int ival;
  9165.  
  9166. cin >> ival;
  9167. cout << dec << ival << " cin.good()=" << cin.good() << '\n';
  9168.  
  9169. cin.clear(); // reset any error
  9170.  
  9171. cin >> setbase(0) >> ival;
  9172. cout << dec << ival <<" cin.good()=" << cin.good() << '\n';
  9173. cin.clear(); // reset any error
  9174.  
  9175. cin >> dec >> ival;
  9176. cout << dec << ival << " cin.good()=" << cin.good() << '\n';
  9177. cin.clear(); // reset any error
  9178.  
  9179. cin >> oct >> ival;
  9180. cout << dec << ival << " cin.good()=" << cin.good() << '\n';
  9181. cin.clear(); // reset any error
  9182.  
  9183. cin >> hex >> ival;
  9184. coat << dec << ival << " cin.good()=" << cin.good() << '\n';
  9185. cin.clear(); // reset any error
  9186.  
  9187. return 0;
  9188. }
  9189.  
  9190. // End of File
  9191.  
  9192.  
  9193. Listing 10 copyprob.c
  9194. /* pseudocode on */
  9195. /* copy characters looking for tab or blank. */
  9196. while (not end of string) {
  9197. if (tab or blank) {
  9198. copy blank and advance pointers
  9199. while (not end of string)
  9200. if (not tab or blank)
  9201. break
  9202. else
  9203. advance from pointer
  9204. }
  9205. /* we are not pointing to a blank or tab */
  9206. copy next character and advance pointers
  9207. }
  9208. terminate string
  9209. /* pseudocode off */
  9210.  
  9211. /* End of File */
  9212.  
  9213.  
  9214. Listing 11 copyfix.c
  9215. while (*from) {
  9216. if (blank or tab) {
  9217. /* stuff deleted. */
  9218. }
  9219. /* its not a blank or tab now. */
  9220. if (*from)
  9221. /* We use terminator to end outer while so
  9222. don't advance past it. */
  9223. /* We force termination after end of outer while so
  9224. don't copy terminator either. */
  9225. *to++ = *from++;
  9226. }
  9227. *to = '\0';
  9228.  
  9229. }
  9230.  
  9231. /* End of File */
  9232.  
  9233.  
  9234. Listing 12 sfxtest.c
  9235. #define TEST_SUFFIX "sfx test"
  9236.  
  9237. main()
  9238. {
  9239. char *sfx; /* to check suffix for violation. */
  9240.  
  9241. /* stuff deleted. */
  9242.  
  9243. do {
  9244.  
  9245. strcpy(buf, testin[testcase]);
  9246. sfx = buf + strlen(buf)+1;
  9247. strcpy(sfx, TEST_SUFFIX);
  9248.  
  9249. /* tested function call return value deleted. */
  9250.  
  9251. if (strcmp(buf,testout[testcase]) != 0) {
  9252. /* error messages deleted. */
  9253. }
  9254. if (strcmp(sfx, TEST_SUFFIX) != {0) {
  9255. /* new error messages. */
  9256. }
  9257. } while (not end of test data);
  9258. /* final report deleted. */
  9259. }
  9260.  
  9261. /* End of File */
  9262.  
  9263.  
  9264. Listing 13 pizza.c
  9265. /* pizza.c */
  9266. #include <stdio.h>
  9267. #include <stdlib.h>
  9268. #include <string.h>
  9269.  
  9270. struct PIZZA
  9271. {
  9272. int key;
  9273. char stuff[10];
  9274. };
  9275.  
  9276. main()
  9277. {
  9278. struct PIZZA *myPizza = calloc(10,sizeof(struct PIZZA));
  9279. myPizza->key = 0;
  9280. strcpy(myPizza->stuff,"good food");
  9281. printf("%d: %s\n",myPizza[0].key,myPizza[0].stuff);
  9282. return 0;
  9283. }
  9284.  
  9285. /* End of File */
  9286.  
  9287.  
  9288.  
  9289. Listing 14 pizza.cpp
  9290. // pizza.cpp
  9291. #include <iostream.h>
  9292. #include <string.h>
  9293.  
  9294. struct PIZZA
  9295. {
  9296. int key;
  9297. char stuff[10];
  9298. };
  9299.  
  9300. main()
  9301. {
  9302. PIZZA *myPizza = new PIZZA[10];
  9303. myPizza->key = 0;
  9304. strcpy(myPizza->stuff,"good food");
  9305. cout << myPizza[0].key << ": "<< myPizza[0].stuff << endl;
  9306. return 0;
  9307. }
  9308.  
  9309. // End of File
  9310.  
  9311.  
  9312.  
  9313.  
  9314.  
  9315.  
  9316.  
  9317.  
  9318.  
  9319.  
  9320.  
  9321.  
  9322.  
  9323.  
  9324.  
  9325.  
  9326.  
  9327.  
  9328.  
  9329.  
  9330.  
  9331.  
  9332.  
  9333.  
  9334.  
  9335.  
  9336.  
  9337.  
  9338.  
  9339.  
  9340.  
  9341.  
  9342.  
  9343.  
  9344.  
  9345.  
  9346.  
  9347.  
  9348.  
  9349.  
  9350.  
  9351.  
  9352. A New Algorithm for Data Compression
  9353.  
  9354.  
  9355. Philip Gage
  9356.  
  9357.  
  9358. Phil Gage is a software engineer in Colorado Springs and has a BS degree in
  9359. computer science from the University of Colorado. He has been a professional
  9360. programmer since 1983 and has used C since 1986. Phil can be reached at (719)
  9361. 593-1801 or via CompuServe as 70541,3645.
  9362.  
  9363.  
  9364. Data compression is becoming increasingly important as a way to stretch disk
  9365. space and speed up data transfers.
  9366. This article describes a simple general-purpose data compression algorithm,
  9367. called Byte Pair Encoding (BPE), which provides almost as much compression as
  9368. the popular Lempel, Ziv, and Welch (LZW) method. (I mention the LZW method in
  9369. particular because it delivers good overall performance and is widely used.)
  9370. BPE's compression speed is somewhat slower than LZW's, but BPE's expansion is
  9371. faster. The main advantage of BPE is the small, fast expansion routine, ideal
  9372. for applications with limited memory. The accompanying C code provides an
  9373. efficient implementation of the algorithm.
  9374.  
  9375.  
  9376. Theory
  9377.  
  9378.  
  9379. Many compression algorithms replace frequently occurring bit patterns with
  9380. shorter representations. The simple approach I present is to replace common
  9381. pairs of bytes by single bytes.
  9382. The algorithm compresses data by finding the most frequently occurring pairs
  9383. of adjacent bytes in the data and replacing all instances of the pair with a
  9384. byte that was not in the original data. The algorithm repeats this process
  9385. until no further compression is possible, either because there are no more
  9386. frequently occurring pairs or there are no more unused bytes to represent
  9387. pairs. The algorithm writes out the table of pair substitutions before the
  9388. packed data.
  9389. This algorithm is fundamentally multi-pass and requires that all the data be
  9390. stored in memory. This requirement causes two potential problems: the
  9391. algorithm cannot handle streams, and some files may be too large to fit in
  9392. memory. Also, large binary files might contain no unused characters to
  9393. represent pair substitutions.
  9394. Buffering small blocks of data and compressing each block separately solves
  9395. these problems. The algorithm reads and stores data until the buffer is full
  9396. or only a minimum number of unused characters remain. The algorithm then
  9397. compresses the buffer and outputs the compressed buffer along with its pair
  9398. table. Using a different pair table for each data block also provides local
  9399. adaptation to varying data and improves overall compression.
  9400. Listing 1 and Listing 2 show pseudocode for the compression and expansion
  9401. algorithms.
  9402.  
  9403.  
  9404. Implementation
  9405.  
  9406.  
  9407. Listing 3 and Listing 4 provide complete C programs for compression and
  9408. expansion of files. The code is not machine dependent and should work with any
  9409. ANSI C compiler. For simplicity, error handling is minimal. You may want to
  9410. add checks for hash table overflow, expand stack overflow and input/output
  9411. errors. The expansion program is much simpler and faster than the compression
  9412. program.
  9413. The compression algorithm spends the most time finding the most frequent pair
  9414. of adjacent characters in the data. The program maintains a hash table
  9415. consisting of arrays left[], right[], and count[] to count pair frequencies.
  9416. The hash table size HASHSIZE must be a power of two, and should not be too
  9417. much smaller than the buffer size BLOCKSIZE or overflow may occur. Programmers
  9418. can adjust the value of BLOCKSIZE for optimum performance, up to a maximum of
  9419. 32767 bytes. The parameter THRESHOLD, which specifies the minimum occurrence
  9420. count of pairs to be compressed, can also be adjusted.
  9421. Once the algorithm finds the most frequently occurring pair, it must replace
  9422. the pair throughout the data buffer with an unused character. The algorithm
  9423. performs this replacement in place within a single buffer. As it replaces each
  9424. pair, the algorithm updates the hash table's pair counts. This method of
  9425. updating the hash table is faster than rebuilding the entire hash table after
  9426. each pair substitution.
  9427.  
  9428.  
  9429. Pair Table Compression
  9430.  
  9431.  
  9432. After the program has compressed a buffer, the pair table contains entries of
  9433. those pairs of bytes that were replaced by single bytes within the buffer.
  9434. Figure 1 shows a sample pair table resulting from compression of a string of 9
  9435. characters, with a hypothetical character set limited to 8 characters. The
  9436. pair table does not store the replacement bytes; rather, a pair's position in
  9437. the table indicates the value of the replacement byte. For example, in Figure
  9438. 1, pair 'A':'B' is found in the pair table's 8th entry, which indicates that
  9439. this particular pair was replaced by the character 'H'. Those entries in the
  9440. pair table not containing a replaced pair are distinguished by a left code
  9441. whose value is equal to its index (index == leftcodef[index]). (Note: The
  9442. compression algorithm uses the array rightcode[] for two different purposes.
  9443. During the initial part of the compression process, function fileread uses the
  9444. rightcode[] array to flag used vs. unused characters. After buffer
  9445. compression, rightcode[] serves as half of the pair table.)
  9446. The algorithm must write the pair substitution tables to the output along with
  9447. the packed data. It would be simple just to write the character code and pair
  9448. for each substitution. Unfortunately, this method would require three bytes
  9449. per code and would waste space. Therefore, this program applies a form of
  9450. encoding to also compress the pair table before it is written to the output.
  9451. To compress the pair table, the program steps through the table from the first
  9452. entry thru its last entry, classifying each entry as representing a replaced
  9453. pair (index != leftcode[index]) or as not representing a replaced pair (index
  9454. == leftcode[index]). To encode a group of contiguous replaced pairs, the
  9455. program emits a positive count byte followed by the pairs. To encode a group
  9456. of contiguous table entries that don't represent replaced pairs, the program
  9457. emits a negative count byte followed by one pair.
  9458. In the encoded pair table a positive count byte indicates to the expansion
  9459. program how many of the following pairs of bytes to read, while a negative
  9460. byte causes the expansion program to skip a range of the character set and
  9461. then read a single pair. This technique allows many pairs to be stored with
  9462. only two bytes per code.
  9463. To further increase pair table compression, I've modified the algorithm from
  9464. the preceding description to avoid disrupting runs of pairs where possible.
  9465. Specifically, the algorithm does not encode an isolated, single byte not
  9466. representing a pair; instead, the algorithm writes the byte to output along
  9467. with the pair data without an accompanying right code. The expansion algorithm
  9468. knows that the byte does not represent pair data because the byte occurs at a
  9469. position such that byte value == leftcode[byte value].
  9470.  
  9471.  
  9472. Expansion
  9473.  
  9474.  
  9475. As opposed to the compression algorithm, which makes multiple passes over the
  9476. data, the expansion algorithm operates in a single pass. You can think of the
  9477. expansion algorithm as a black box which obtains input bytes from one of two
  9478. sources, the input file, or a stack (see Figure 2). Regardless of an input
  9479. byte's source, the algorithm processes each byte according to the following
  9480. rule: if the byte is a literal, the algorithm passes it to the output; if the
  9481. byte represents a pair, the algorithm replaces it with a pair and pushes the
  9482. pair onto the stack.
  9483. Now, to complete the loop, the algorithm selects its input source according to
  9484. the following rule: If the stack contains data, the algorithm obtains its next
  9485. input byte from the stack. If the stack is empty, the algorithm obtains its
  9486. next input byte from the input file.
  9487. The effect of these rules is "local" expansion of byte pairs; that is, if a
  9488. byte expands to a pair, and that pair contains one or more bytes in need of
  9489. expansion, the algorithm will expand the newly created bytes before it reads
  9490. any more from the input file.
  9491.  
  9492.  
  9493. Advantages of BPE
  9494.  
  9495.  
  9496. One significant advantage of the BPE algorithm is that compression never
  9497. increases the data size. This guarantee makes BPE suitable for real-time
  9498. applications where the type of data to be compressed may be unknown. If no
  9499. compression can be performed, BPE passes the data through unchanged except for
  9500. the addition of a few header bytes to each block of data. Some algorithms,
  9501. including LZW, can greatly inflate the size of certain data sets, such as
  9502. randomized data or pre-compressed files.
  9503. LZW compression adapts linearly to frequently occurring patterns, building up
  9504. strings one character at a time. The BPE algorithm adapts exponentially to
  9505. patterns, since both bytes in a pair can represent previously defined pair
  9506. codes. The previously defined pair codes can themselves contain nested codes
  9507. and can expand into long strings. This difference between LZW and BPE provides
  9508. better compression for BPE in some cases. For example, under BPE a run of 1024
  9509. identical bytes in a row is reduced to a single byte after only ten pair
  9510. substitutions. This nesting of pair codes is the real power of the algorithm.
  9511. The following example illustrates this process:
  9512. Original input data string: ABABCABCD
  9513. Change pair AB to unused X: XXCXCD
  9514. Change pair XC to unused Y: XYYD
  9515.  
  9516. Finally, both BPE's compression and expansion algorithms require little memory
  9517. for data arrays, 5 to 30K for compression and only 550 bytes for expansion.
  9518. The expansion routine is so simple that, coded in assembler, it should require
  9519. only about 2K of memory for all code and data.
  9520.  
  9521.  
  9522. Results
  9523.  
  9524.  
  9525. The BPE program delivers performance comparable to LZW, as shown in Table 1. I
  9526. compressed and expanded what I consider to be a typical binary file, the
  9527. Windows 3.1 program WIN386.EXE. I measured the timing on a 33MHz 486DX PC
  9528. compatible using MS-DOS 5.0 and Borland C++ 3.0.
  9529. I tested the BPE program against the LZW program, LZW15V.C, from The Data
  9530. Compression Book by Mark Nelson, using 12-bit codes with a 5021 entry hash
  9531. table and 14-bit codes with a 18041 entry hash table. The 12-bit version uses
  9532. less memory for data but does not compress quite as well. I also tested
  9533. several other LZW programs and obtained similar results.
  9534. The Default BPE column shows the results of using the default parameters from
  9535. Listing 3, which are tuned for good performance on all types of files,
  9536. including binary and text. Although BPE packed this binary file slightly
  9537. better than LZW, performance will vary on other files depending on the type of
  9538. data.
  9539. The Small BPE column shows the results of reducing the amount of memory
  9540. available for the compression program data arrays. I changed BLOCKSIZE from
  9541. 5000 to 800 and HASHSIZE from 4096 to 1024. These changes only slightly
  9542. decreased the compression ratio on the binary file, but the smaller buffer
  9543. size will not work as well on text files.
  9544. The Fast BPE column shows the results of increasing compression speed, by
  9545. changing THRESHOLD from 3 to 10. This change caused the program to skip pairs
  9546. with less than 10 occurrences. Since the program compresses most frequently
  9547. occurring pairs first, skipping low-frequency pairs near the end of block
  9548. processing has little effect on the amount of compression but can
  9549. significantly improve speed. This change reduced the compression time from 55
  9550. to 30 seconds.
  9551.  
  9552.  
  9553. Enhancing BPE
  9554.  
  9555.  
  9556. The BPE algorithm could be enhanced by block size optimization. The block size
  9557. is critical to both the compression ratio and speed. Large blocks work better
  9558. for text, small blocks work better for binary data.
  9559.  
  9560.  
  9561. Conclusion
  9562.  
  9563.  
  9564. It's surprising that the BPE algorithm works as well as it does, considering
  9565. that it discards all information on previous data and does not use
  9566. variable-sized bit codes, contrary to many modern compression techniques. The
  9567. BPE compression algorithm is useful for applications requiring simple, fast
  9568. expansion of compressed data, such as self-extracting programs, image display,
  9569. communication links and embedded systems. Advantages include a small expansion
  9570. routine, low memory usage, tunable performance, and good performance on
  9571. worst-case data. The disadvantages of BPE are slow compression speed and a
  9572. lower compression ratio than provided by some of the commonly used algorithms,
  9573. such as LZW. Even with these disadvantages, BPE is a worthwhile technique to
  9574. have at your disposal.
  9575.  
  9576.  
  9577. Bibliography
  9578.  
  9579.  
  9580. 1) J. Ziv and A. Lempel, "A Universal Algorithm for Sequential Data
  9581. Compression," IEE Transactions on Information Theory, May 1977.
  9582. 2) T. Welch, "A Technique for High-Performance Data Compression," Computer,
  9583. June 1984.
  9584. 3) M. Nelson, The Data Compression Book, M&T Books, 1991.
  9585. Figure 1 Illustration of compression process with hypothetical character set
  9586. Figure 2 Illustration of expansion process
  9587. Table 1 Comparison of LZW and BPE performance
  9588.  12 bit 14 bit Default Small Fast
  9589.  LZW LZW BPE BPE BPE
  9590. -------------------------------------------------------------------
  9591. Original file
  9592.  size (bytes) 544,789 544,789 544,789 544,789 544,789
  9593. Compressed file
  9594.  size (bytes) 299,118 292,588 276,955 293,520 295,729
  9595. Compression time
  9596.  (secs) 28 28 55 41 30
  9597. Expansion time
  9598.  (secs) 27 25 20 21 19
  9599. Compression data
  9600.  size (bytes) 25,100 90,200 17,800 4,400 17,800
  9601. Expansion data
  9602.  size (bytes) 20,000 72,200 550 550 550
  9603.  
  9604. Listing 1 Compression algorithm (pseudocode)
  9605. While not end of file
  9606. Read next block of data into buffer and
  9607. enter all pairs in hash table with counts of their occurrence
  9608. While compression possible
  9609. Find most frequent byte pair
  9610. Replace pair with an unused byte
  9611. If substitution deletes a pair from buffer,
  9612. decrease its count in the hash table
  9613.  
  9614. If substitution adds a new pair to the buffer,
  9615. increase its count in the hash table
  9616. Add pair to pair table
  9617. End while
  9618. Write pair table and packed data
  9619.  
  9620. End while
  9621.  
  9622.  
  9623. Listing 2 Expansion algorithm (pseudocode)
  9624. While not end of file
  9625. Read pair table from input
  9626. While more data in block
  9627. If stack empty, read byte from input
  9628. Else pop byte from stack
  9629. If byte in table, push pair on stack
  9630. Else write byte to output
  9631. End while
  9632.  
  9633. End while
  9634.  
  9635.  
  9636. Listing 3 Compression program
  9637. /* compress.c */
  9638. /* Copyright 1994 by Philip Gage */
  9639.  
  9640. #include <stdio.h>
  9641.  
  9642. #define BLOCKSIZE 5000 /* Maximum block size */
  9643. #define HASHSIZE 4096 /* Size of hash table */
  9644. #define MAXCHARS 200 /* Char set per block */
  9645. #define THRESHOLD 3 /* Minimum pair count */
  9646.  
  9647. unsigned char buffer[BLOCKSIZE]; /* Data block */
  9648. unsigned char leftcode[256]; /* Pair table */
  9649. unsigned char rightcode[256]; /* Pair table */
  9650. unsigned char left[HASHSIZE]; /* Hash table */
  9651. unsigned char right[HASHSIZE]; /* Hash table */
  9652. unsigned char count[HASHSIZE]; /* Pair count */
  9653. int size; /* Size of current data block */
  9654.  
  9655. /* Function prototypes */
  9656. int lookup (unsigned char, unsigned char);
  9657. int fileread (FILE *);
  9658. void filewrite (FILE *);
  9659. void compress (FILE *, FILE *);
  9660.  
  9661. /* Return index of character pair in hash table */
  9662. /* Deleted nodes have count of 1 for hashing */
  9663. int lookup (unsigned char a, unsigned char b)
  9664. {
  9665. int index;
  9666.  
  9667. /* Compute hash key from both characters */
  9668. index= (a ^ (b << 5)) & (HASHSIZE-1);
  9669.  
  9670. /* Search for pair or first empty slot */
  9671. while ((left[index[ != a right[index] != b) &&
  9672. count[index] != 0)
  9673.  
  9674. index = (index + 1) & (HASHSIZE-1);
  9675.  
  9676. /* Store pair in table */
  9677. left[index] = a;
  9678. right[index]= b;
  9679. return index;
  9680. }
  9681.  
  9682. /* Read next block from input file into buffer */
  9683. int fileread (FILE *input)
  9684. {
  9685. int c, index, used=0;
  9686.  
  9687. /* Reset hash table and pair table */
  9688. for (c = 0; c < HASHSIZE; c++)
  9689. count[c] = 0;
  9690. for (c = 0; c < 256; c++) {
  9691. leftcode[c] = c;
  9692. rightcode[c] = 0;
  9693. }
  9694. size= 0;
  9695.  
  9696. /* Read data until full or few unused chars */
  9697. while (size < BLOCKSIZE && used < MAXCHARS &&
  9698. (c = getc(input)) != EOF) {
  9699. if (size > 0) {
  9700. index = lookup(buffer[size-1],c);
  9701. if (count[index] < 255) ++count[index];
  9702. }
  9703. buffer[size++] = c;
  9704.  
  9705. /* Use rightcode to flag data chars found */
  9706. if (!rightcode[c]) {
  9707. rightcode[c] = 1;
  9708. used++;
  9709. }
  9710. }
  9711. return c == EOF;
  9712. }
  9713.  
  9714. /* Write each pair table and data block to output */
  9715. void filewrite (FILE *output)
  9716. {
  9717. int i, len, c = 0;
  9718.  
  9719. /* For each character 0..255 */
  9720. while (c < 256) {
  9721.  
  9722. /* If not a pair code, count run of literals */
  9723. if (c == leftcode[c]) {
  9724. len = 1; c++;
  9725. while (len<127 && c<256 && c==leftcode[c]) {
  9726. len++; c++;
  9727. }
  9728. putc(len + 127,output); len = 0;
  9729. if (c == 256) break;
  9730. }
  9731.  
  9732. /* Else count run of pair codes */
  9733.  
  9734. else {
  9735. len = 0; c++;
  9736. while (len<127 && c<256 && c!=leftcode[c] 
  9737. len<125 && c<254 && c+1!=leftcode[c+1]) {
  9738. len++; c++;
  9739. }
  9740. putc(len,output);
  9741. c -= len + 1;
  9742. }
  9743.  
  9744. /* Write range of pairs to output */
  9745. for (i = 0; i <= len; i++) {
  9746. putc(leftcode[c],output);
  9747. if (c != leftcode[c])
  9748. putc(rightcode[c],output);
  9749. c++;
  9750. }
  9751. }
  9752.  
  9753. /* Write size bytes and compressed data block */
  9754. putc(size/256,output);
  9755. putc(size%256,output);
  9756. fwrite(buffer,size,1,output);
  9757. }
  9758.  
  9759. /* Compress from input file to output file */
  9760. void compress (FILE *infile, FILE *outfile)
  9761. {
  9762. int leftch, rightch, code, oldsize;
  9763. int index, r, w, best, done = 0;
  9764.  
  9765. /* Compress each data block until end of file */
  9766. while (!done) {
  9767.  
  9768. done = fileread(infile);
  9769. code = 256;
  9770.  
  9771. /* Compress this block */
  9772. for (;;) {
  9773.  
  9774. /* Get next unused char for pair code */
  9775. for (code--; code >= 0; code--)
  9776. if (code==leftcode[code] && !rightcode[code])
  9777. break;
  9778.  
  9779. /* Must quit if no unused chars left */
  9780. if (code < 0) break;
  9781.  
  9782. /* Find most frequent pair of chars */
  9783. for (best=2, index=0; index<HASHSIZE; index++)
  9784. if (count[index] > best) {
  9785. best = count[index];
  9786. leftch = left[index];
  9787. rightch = right[index];
  9788. }
  9789.  
  9790. /* Done if no more compression possible */
  9791. if (best < THRESHOLD) break;
  9792.  
  9793.  
  9794. /* Replace pairs in data, adjust pair counts */
  9795. oldsize = size - 1;
  9796. for (w = 0, r = 0; r < oldsize; r++) {
  9797. if (buffer[r] == leftch &&
  9798. buffer[r+1] == rightch) {
  9799.  
  9800. if (r > 0) {
  9801. index = lookup(buffer[w-1],leftch);
  9802. if (count[index] > 1) --count[index];
  9803. index = lookup(buffer[w-1],code);
  9804. if (count[index] < 255) ++count[index];
  9805. }
  9806. if (r < oldsize - 1) {
  9807. index = lookup(rightch,buffer[r+2]);
  9808. if (count[index] > 1) --count[index];
  9809. index = lookup(code,buffer[r+2]);
  9810. if (count[index] < 255) ++count[index];
  9811. }
  9812. buffer[w++] = code;
  9813. r++; size--;
  9814. }
  9815. else buffer[w++] = buffer[r];
  9816. }
  9817. buffer[w] = buffer[r];
  9818.  
  9819. /* Add to pair substitution table */
  9820. leftcode[code] = leftch;
  9821. rightcode[code] = rightch;
  9822.  
  9823. /* Delete pair from hash table */
  9824. index = lookup(leftch,rightch);
  9825. count[index] = 1;
  9826. }
  9827. filewrite(outfile);
  9828. }
  9829. }
  9830.  
  9831. void main (int argc, char *argv[])
  9832. {
  9833. FILE *infile, *outfile;
  9834.  
  9835. if (argc != 3)
  9836. printf("Usage: compress infile outfile\n");
  9837. else if ((infile=fopen(argv[1],"rb"))==NULL)
  9838. printf("Error opening input %s\n",argv[1]);
  9839. else if ((outfile=fopen(argv[2],"wb"))==NULL)
  9840. printf("Error opening output %s\n",argv[2]);
  9841. else {
  9842. compress(infile,outfile);
  9843. fclose(outfile);
  9844. fclose(infile);
  9845. }
  9846. }
  9847.  
  9848. /*End of File */
  9849.  
  9850.  
  9851. Listing 4 Expansion program
  9852. /* expand.c */
  9853.  
  9854. /* Copyright 1994 by Philip Gage */
  9855.  
  9856. #include <stdio.h>
  9857.  
  9858. /* Decompress data from input to output */
  9859. void expand (FILE *input, FILE *output)
  9860. {
  9861. unsigned char left[256], right[256], stack[30];
  9862. short int c, count, i, size;
  9863.  
  9864. /* Unpack each block until end of file */
  9865. while ((count = getc(input)) != EOF) {
  9866.  
  9867. /* Set left to itself as literal flag */
  9868. for (i = 0; i < 256; i++)
  9869. left[i] = i;
  9870.  
  9871. /* Read pair table */
  9872. for (c = 0;;) {
  9873.  
  9874. /* Skip range of literal bytes */
  9875. if (count > 127) {
  9876. c += count - 127;
  9877. count = 0;
  9878. }
  9879. if (c == 256) break;
  9880.  
  9881. /* Read pairs, skip right if literal */
  9882. for (i = 0; i <= count; i++, c++) {
  9883. left[c] = getc(input);
  9884. if (c != left[c])
  9885. right[c] = getc(input);
  9886. }
  9887. if (c == 256) break;
  9888. count = getc(input);
  9889. }
  9890.  
  9891. /* Calculate packed data block size */
  9892. size = 256 * getc(input) + getc(input);
  9893.  
  9894. /* Unpack data block */
  9895. for (i = 0;;) {
  9896.  
  9897. /* Pop byte from stack or read byte */
  9898. if (i)
  9899. c = stack[--i];
  9900. else {
  9901. if (!size--) break;
  9902. c = getc(input);
  9903. }
  9904.  
  9905. /* Output byte or push pair on stack */
  9906. if (c == left[c])
  9907. putc(c,output);
  9908. else {
  9909. stack[i++] = right[c];
  9910. stack[i++] = left[c];
  9911. }
  9912. }
  9913.  
  9914. }
  9915. }
  9916.  
  9917. void main (int argc, char *argv[])
  9918. {
  9919. FILE *infile, *outfile;
  9920. if (argc != 3)
  9921. printf("Usage: expand infile outfile\n");
  9922. else if ((infile=fopen(argv[1],"rb"))==NULL)
  9923. printf("Error opening input %s\n",argv[1]);
  9924. else if ((outfile=fopen(argv[2],"wb"))==NULL)
  9925. printf("Error opening output %s\n",argv[2]);
  9926. else {
  9927. expand(infile,outfile);
  9928. fclose(outfile);
  9929. fclose(infile);
  9930. }
  9931. }
  9932. /* End of File */
  9933.  
  9934.  
  9935.  
  9936.  
  9937.  
  9938.  
  9939.  
  9940.  
  9941.  
  9942.  
  9943.  
  9944.  
  9945.  
  9946.  
  9947.  
  9948.  
  9949.  
  9950.  
  9951.  
  9952.  
  9953.  
  9954.  
  9955.  
  9956.  
  9957.  
  9958.  
  9959.  
  9960.  
  9961.  
  9962.  
  9963.  
  9964.  
  9965.  
  9966.  
  9967.  
  9968.  
  9969.  
  9970.  
  9971.  
  9972.  
  9973.  
  9974.  
  9975.  
  9976.  
  9977. Two Fast Pattern-Matching Algorithms
  9978.  
  9979.  
  9980. Erick Otto
  9981.  
  9982.  
  9983. Erick Otto has a degree in computer science and has been working in the
  9984. customer support group for the 5ESS telephone switch since 1985. He has been
  9985. programming in C since 1984 and in the last four years has been involved in
  9986. programming a Customer Complaints Database for AT&T switching products. His
  9987. particular interests are String/Pattern searching and techniques to make C
  9988. programs run as fast as possible. He is currently working as supervisor of
  9989. AT&T-NS local field support in Indonesia. He may be reached at
  9990. eotto@lfsin.idgtw.attibr.att.com or com!att!attibr!idgtw!lfsin!eotto.
  9991.  
  9992.  
  9993.  
  9994.  
  9995. Introduction
  9996.  
  9997.  
  9998. This article introduces two algorithms that are very fast in finding strings
  9999. in large text files. I have implemented them to perform string searching in a
  10000. text database of over 120 Megabytes. I needed algorithms that were very fast
  10001. and flexible, because the normal -- and still very much used -- one-by-one
  10002. character comparison method was too slow to be practical. The algorithms I
  10003. discuss here are about ten times faster on average than the "normal" UNIX
  10004. grep.
  10005. The first algorithm is actually the faster. It is based on a tuned Boyer and
  10006. Moore algorithm [2] further developed by Hume and Sunday [3]. The second
  10007. algorithm, called "shift-OR," is based on algorithms found by Beaza-Yates and
  10008. Gonnet [1] and further developed by Wu and Manber. It provides more
  10009. flexibility, including regular-expression searching without much loss of
  10010. speed.
  10011.  
  10012.  
  10013. The Boyer and Moore Algorithm
  10014.  
  10015.  
  10016. The basics of the first algorithm were first found by Boyer and Moore in 1977
  10017. [2]. The original goal was to make as few character comparisons as possible in
  10018. order to speed up the search. They found that a low number of character
  10019. comparisons usually means that the program is faster. By using efficient
  10020. programming, and testing various constructions of algorithms, a higher
  10021. execution speed can be obtained.
  10022. One of the differences from grep is that the algorithm makes use of
  10023. information retrieved from the pattern itself. Knowing that a certain
  10024. character does not occur in the pattern is useful information. One other
  10025. difference from grep is that the characters are compared fight to left.
  10026. The character comparison starts at the last character of the pattern and the
  10027. related position of that character in the text. (See Figure 1a.) In other
  10028. words, if we are positioned in the text at the character x, and the pattern we
  10029. are looking for is today, there is no possibility that we can have a match at
  10030. this position since x does not occur in the pattern.
  10031. In order to know which characters occur in the pattern and which do not, we
  10032. build a table dist, which contains the distances of every character in the
  10033. pattern calculated from the end. The characters that do not occur in the
  10034. pattern are set to the length of the pattern. (See Figure 2.) This is another
  10035. difference from grep. We have some overhead in pre-processing the pattern.
  10036. Now we can look up the character x in the table and find that the distance is
  10037. 5. This means we can advance our text pointer by 5. (See Figure 1b.) The
  10038. pointer into the pattern is untouched. We start the comparison again with the
  10039. new pointer into the text.
  10040. Now consider another situation, where the text character is an a. We can
  10041. advance only dist[a], or one character in the text. That way, the second
  10042. character on the left of both the text and the pattern would be the character
  10043. a. (See Figure 1c.)
  10044. Using this algorithm speeds up the search considerably because not every
  10045. character in the text is compared with the pattern. It will at first only look
  10046. for a starting position in the text to start the full pattern comparison.
  10047. Another nifty trick that is used at this point is to see if the character that
  10048. occurs the least frequently in the text (the lfc, for short) is present where
  10049. we expect it. If we assume that m is the lfc in the pattern memorandum, we can
  10050. do a pre-check on the leftmost occurrence of the lfc in the text. See Figure
  10051. 3. This figure also shows at what positions which elements of the algorithms
  10052. are used. If the pre-test is satisfied, we perform a normal match, comparing
  10053. every character in the pattern with every character in the text.
  10054. Finally the last basic step is to go forward in the text after we tested for a
  10055. possible match. After extensive research performed by Hume and Sunday [3], the
  10056. shift used is based on the first leftward re-occurrence of the last character
  10057. of the pattern. If there is no such re-occurrence we forward by the pattern
  10058. length.
  10059. This algorithm is very fast, but depends on the length of the pattern and the
  10060. frequency of the characters in the pattern. Listing 1 shows my implementation
  10061. of the Boyer and Moore search algorithm. Listing 3 shows a small program that
  10062. will generate a frequency distribution derived from a file.
  10063.  
  10064.  
  10065. The Shift-OR Algorithm
  10066.  
  10067.  
  10068. The other algorithm that I present is based on binary operations instead of
  10069. character comparisons. This is faster than grep because mostly binary
  10070. operations are performed.
  10071. In this method we track the state of the search by using binary operations.
  10072. The state variable will tell us how many characters have matched to the left
  10073. of the current position in the text. To do this we need to maintain a table
  10074. that contains information obtained from the pattern. The position of every
  10075. character that occurs in the pattern gets a zero set in the table entry for
  10076. that character. (See Figure 4.)
  10077. The characters are counted from left to right and the bits are counted from
  10078. right to left. Since the character e occurs twice in the pattern stereo, it
  10079. gets two bits set to zero. Characters that do not occur in the pattern get all
  10080. ones set in the table. The initial state gets set to all ones. To perform the
  10081. comparison we first shift the state left one bit. (In C, this mean insert a
  10082. zero.) We then bitwise-OR the table entry for that character in the text. It
  10083. can be envisioned as a window, opening (zero) when comparing a certain
  10084. position.
  10085. Suppose that the current character in the text is s. We first shift the state
  10086. left one bit and bitwise-OR in T[s]. The result is the new state of the
  10087. search, which has bit zero set to zero. This means that at the current
  10088. position in the text there is one match of length one. (See Figure 5a.)
  10089. If the word stereo is in the text and we are positioned in the text at the
  10090. second e, the state will be 1...101111. This means that there is one match of
  10091. length five left of the current position. (See Figure 5b.)
  10092. The way to decide if we have found a match is to set another variable to the
  10093. length of the pattern (stereo has length 6) in the appropriate way -- in this
  10094. case to 1...000000. Now, if the state is less than the stop variable, we have
  10095. found a match (1...0111111..1000000).
  10096. The flexibility of this algorithm comes from the fact that we can easily
  10097. manipulate the character table T. If we set the second bit from the right to a
  10098. zero for every entry, this means we don't care what the second character of
  10099. the pattern is. This is the so-called wildcard search. And the nice thing
  10100. about this is that it doesn't cost a penny more to perform.
  10101. We can apply the same logic to ranges of characters. We could for example set
  10102. the second bit in table T for the characters t, p, and f to a zero. This would
  10103. match the words stereo, spereo, and sfereo in the text. You could even make
  10104. use of the information in the state to report partial matches.
  10105. This algorithm is slower than the previous one but does not depend on the
  10106. length of the pattern or the frequency of the characters. It can also be used
  10107. in a much more flexible way. Listing 2 shows my implementation of the shift-OR
  10108. search algorithm.
  10109.  
  10110.  
  10111. Implementations
  10112.  
  10113.  
  10114. The code presented hides a few tricks I would like to mention. I have put a
  10115. lot of comments in the code to make clear what is happening. First the speed
  10116. of a program like this also depends on the I/O mechanism that is used. In line
  10117. 20 in the first program (Listing 1) I therefore use the system-defined buffer
  10118. size. This will reduce the number of I/O accesses. Usually, using buffers less
  10119. than the system buffer size will cause unnecessary I/O.
  10120. Further, in line 74 you will see a technique used to keep the number of tests
  10121. inside a loop as small as possible. It usually saves a lot of execution time
  10122. setting sentinels at the end or beginning of a buffer when searching for a
  10123. character, instead of testing inside your loop for reaching the beginning or
  10124. the end of your buffer.
  10125. In line 80 we make sure that we end the text search on a line boundary, to
  10126. prevent the match routine from getting half words. The build routine does the
  10127. preparation work for the pattern search. It initializes the distance table,
  10128. finds the lowest frequency character, and sets the shift. In line 176 and 177
  10129. again you see the sentinel technique, although a little different than before.
  10130. We need to put as many characters at the end of the buffer as the text pointer
  10131. can be forwarded. (See Figure 3.)
  10132. The other two more interesting points are the code lines at line 191-193 and
  10133. line 234. At line 191 a technique called loop unrolling is used. Loop
  10134. unrolling prevents a lot of branching (for iterations through the loop). On
  10135. modern pipelined machines, the code is usually faster since most probably all
  10136. the instructions can be contained in the instruction cache. Note that when on
  10137. line 191 k is zero, lines 192-193 will also return k as zero, which is a
  10138. prerequisite of using loop unrolling in this case. On different machine
  10139. architectures, unrolling less or more times can be benificial. You can play
  10140. around with it.
  10141. Last, on line 234 we forward the text to the end of the line if we reported a
  10142. match. Since we already printed the full line, why should we search for more
  10143. occurences of the pattern? If you want to count the number of matches, this
  10144. line should be removed!
  10145. This program can also be easily adapted to do case-insensitive searches. The
  10146. pattern will have to be converted to lower case. All table entries for
  10147. characters occuring in the pattern will have to be set to their distances, but
  10148. also their upper case counterparts will have to be set. Besides that, lines
  10149. 205 and 209 will have to be changed so that references to the text are
  10150. translated to lower case. The speed of the program is hardly lowered if this
  10151. is done with a translation table using something like:
  10152. char TR[MAXSYM];
  10153.  
  10154. /* build the case TRanslation table
  10155. */
  10156. for(i=0; i < MAXSYM; i++)
  10157. TR[i] = i;
  10158. for(i='A'; i <= 'Z'; i++)
  10159. TR[i] = i + 'a' - 'A';
  10160. The shift-OR program (Listing 2) uses the same efficient programming
  10161. techniques. Starting at line 13, the size of a "word" (unsigned int) is
  10162. defined. This is needed since the number of bits in a word is also the limit
  10163. for the maximum pattern length that can be used. Of course you can use two
  10164. words (this will slow the algorithm down), but the function matchpat will have
  10165. to be altered.
  10166. The build function in line 80, does the pre-processing of the pattern. I have
  10167. included a small subset of the normal regular-expression pattern matching. The
  10168. build function can handle a wildcard (dot or .) and ranges such as [a-z]. I am
  10169. sure more inventive ways can be found to implement this, but this one works
  10170. fine. In line 242, you can see one limitation that this implementation has.
  10171. The first character needs to be normal, not a regular expression
  10172. metacharacter. This actually speeds things up. Finally, on line 250, you see
  10173. the actual binary operations being performed.
  10174. References
  10175. [1] R. Baeza-Yates and G. H. Gonnet. "A New Approach to Text Searching,"
  10176. Communications of the ACM, Oct. 1992, Vol 35, No 10.
  10177. [2] R.S. Boyer and J.S. Moore. "A Fast String Searching Algorithm,"
  10178. Communications of the ACM, Oct. 1977, Vol 20, No 10.
  10179. [3] A. Hume and D. Sunday. "Fast String Searching," Software, Practice and
  10180. Experience, Fall 1991.
  10181. Figure 1 Text searching with a distance table
  10182. Figure 2 The distance table used in Figure 1
  10183.  âˆ™
  10184.  âˆ™
  10185.  âˆ™
  10186. dist[a] = 1
  10187.  âˆ™
  10188.  âˆ™
  10189. dist[d] = 2
  10190.  âˆ™
  10191.  âˆ™
  10192. dist[o] = 3
  10193.  âˆ™
  10194.  âˆ™
  10195. dist[x] = 5
  10196. dist[y] = 0
  10197. dist[z] = 5
  10198.  âˆ™
  10199.  âˆ™
  10200.  âˆ™
  10201. Figure 3 Detailed illustration of Boyer and Moore text search, with fast
  10202. forwarding, lfc comparison, and shifting
  10203. Figure 4 A table for the Baeza-Yates and Gonnet algorithm
  10204. Pattern = stereo
  10205.  
  10206. T[a] = 1...111111
  10207.  
  10208.  âˆ™
  10209.  âˆ™
  10210.  
  10211. T[e] = 1...101011
  10212. T[o] = 1...011111
  10213. T[r] = 1...110111
  10214. T[s] = 1...111110
  10215. T[t] = 1...111101
  10216.  
  10217.  âˆ™
  10218.  âˆ™
  10219.  âˆ™
  10220. Figure 5 The shift-OR process
  10221. Figure 5a
  10222. initial state = 1...111111
  10223.  
  10224. state<<1 = 1...111110
  10225. T[s] = 1...111110
  10226.  ------------- or
  10227. new state 1...111110
  10228. Figure 5b
  10229.  
  10230. state<<1 = 1...101110
  10231. T[e] = 1...101011
  10232.  ------------ or
  10233. new state 1...101111
  10234.  
  10235. Listing 1 The Boyer and Moore algorithm
  10236. /************************************************
  10237. * Fast String Search
  10238. * Using a tuned Boyer and Moore algorithm.
  10239. * This source code is written by Erick Otto. Algorithms from
  10240. * Daniel Sunday and Andrew Hume are used in this code.
  10241. * 1993 by Erick Otto.
  10242. */
  10243.  
  10244. #include <stdio.h>
  10245. #include <fcntl.h>
  10246. #include "freq.h"
  10247.  
  10248. /* Size of text buffer, BUFSIZ */
  10249. /* is optimum from stdio.h */
  10250. #define TBUF (8*BUFSIZ)
  10251.  
  10252. #define MAXPAT 256
  10253.  
  10254. /* We use the following variables for the search */
  10255. /* pat : The pattern */
  10256. /* dist : A table which contains the offsets */
  10257. /* for every char in the pattern from */
  10258. /* the end of the pattern */
  10259. /* lfc : The least frequent char in the patt */
  10260. /* lfcoff : The offset of lfc from end of patt */
  10261. /* shift : The leftward re-occurance of the */
  10262. /* last charater in positions from the */
  10263. /* end of the patt */
  10264.  
  10265. int patlen;
  10266. unsigned char pat[MAXPAT];
  10267. unsigned char dist[MAXPAT];
  10268. int lfc, lfcoff;
  10269. int shift;
  10270.  
  10271. main(argc, argv)
  10272. int argc;
  10273. char **argv;
  10274. {
  10275. char buf[TBUF+MAXPAT];
  10276. char match[MAXPAT];
  10277. register int fd, nread;
  10278. register char *beg, *lastnl, *end;
  10279. char filename[80];
  10280.  
  10281. /* must be at least one arguments */
  10282. if (argc < 2 ) {
  10283. fprintf(stderr,"\n\tMatch string expected\n");
  10284. exit();
  10285. }
  10286. if (argc < 3 ) {
  10287. fprintf(stderr,"\n\tFilename expected\n");
  10288. exit();
  10289.  
  10290. }
  10291.  
  10292. strcpy(match, argv[1]);
  10293. strcpy(filename, argv[2]);
  10294. build(match, strlen(match));
  10295.  
  10296. if ((fd = open(filename,O_RDONLY)) == -1) {
  10297. fprintf(stderr,
  10298. "Can't open file %s\n", filename);
  10299. perror(filename);
  10300. exit(1);
  10301. }
  10302.  
  10303. *buf = '\n'; /* sentinel for printing purposes */
  10304. beg = buf+1;
  10305.  
  10306. while((nread=read(fd,beg,(&buf[TBUF]-beg)))>0){
  10307. end = beg+nread;
  10308. lastnl = end;
  10309. while(*--lastnl != '\n') ;
  10310.  
  10311. /* lastnl points to the newline */
  10312. matchpat(buf+1, lastnl-buf);
  10313.  
  10314. memcpy(buf+1, lastnl, end-lastnl-1);
  10315. beg = buf + 1 + (end-lastnl);
  10316. }
  10317. close(fd);
  10318. }
  10319.  
  10320. build(match, m)
  10321. unsigned char *match;
  10322. register m;
  10323. {
  10324. register unsigned char *patend, *patptr;
  10325. register unsigned char *d;
  10326. register int i;
  10327. register unsigned char *pos;
  10328. unsigned char *lastchar;
  10329. int lfcidx;
  10330.  
  10331. if(m > MAXPAT) {
  10332. printf("Length of the pattern too long!\n");
  10333. exit(1);
  10334. }
  10335.  
  10336. /* initialize the pattern variables */
  10337. patlen = m;
  10338. memcpy(pat, match, patlen);
  10339.  
  10340. /* build the dist table */
  10341. d = dist;
  10342.  
  10343. /* initialize all elements to the pattern length */
  10344. for(i = 0; i < MAXPAT; i++)
  10345. d[i] = patlen;
  10346.  
  10347. /* set all characters in the pattern to their */
  10348. /* relative distances from the end of the pattern */
  10349.  
  10350.  
  10351. patptr = pat;
  10352. patend = patptr+patlen-1;
  10353.  
  10354. while(patptr < patend) {
  10355. d[*patptr] = patend-patptr;
  10356. patptr++;
  10357. }
  10358. d[*patptr] = 0;
  10359.  
  10360. /* find the least frequent character */
  10361. /* that occurs in the pattern */
  10362.  
  10363. lfcidx = 0;
  10364. for(i = 1; i < patlen; i++){
  10365. if(freq[pat[i]] < freq[pat[lfcidx]])
  10366. lfcidx = i;
  10367. }
  10368.  
  10369. /* set the lf char and offset for later use */
  10370. lfc = pat[lfcidx];
  10371. lfcoff = lfcidx - (patlen-1);
  10372.  
  10373. /* Look backward and see if we can find an other */
  10374. /* occurance of the same character as the last one */
  10375. lastchar = patptr;
  10376. for(pos = lastchar-1; pos >= pat; pos--)
  10377. if (*pos == *lastchar) break;
  10378.  
  10379. /* record the occurance for later use */
  10380. shift = lastchar - pos;
  10381. }
  10382.  
  10383. matchpat(text, n)
  10384. unsigned char *text;
  10385. int n;
  10386. {
  10387. register unsigned char *e, *s;
  10388. register unsigned char *dp;
  10389. register int k;
  10390. register int lfco, lfcc;
  10391. register unsigned char *p, *q;
  10392. register unsigned char *ep;
  10393. register char *sp;
  10394. register char *nl;
  10395. register int t1;
  10396. char save[MAXPAT];
  10397.  
  10398. dp = dist;
  10399. t1 = patlen-1;
  10400. sp = save;
  10401. s = text+t1;
  10402. e = text+n;
  10403.  
  10404. memcpy(sp, e, patlen);
  10405. memset(e, pat[t1], patlen);
  10406. lfco = lfcoff;
  10407. lfcc = lfc;
  10408. ep = pat + t1;
  10409.  
  10410.  
  10411. while(s < e){
  10412.  
  10413. /* fast forward through the text untill we find */
  10414. /* the last char of the pattern (unrolled loop) */
  10415.  
  10416. k = dp[*s];
  10417. while(k){
  10418. k = dp[*(s += k)];
  10419. k = dp[*(s += k)];
  10420. k = dp[*(s += k)];
  10421. }
  10422.  
  10423. /* If we hit the sentinel at the end of */
  10424. /* the buffer we are done with the buffer */
  10425. if(s >= e)
  10426. break;
  10427.  
  10428. /* Neat little trick, actually makes things */
  10429. /* faster. Do a pre check on the lfc, */
  10430. /* before starting the full comparison */
  10431. if(s[lfco] != lfcc)
  10432. goto mismatch;
  10433.  
  10434. /* normal forward string matching */
  10435. for(p = pat, q = s-t1; p < ep; ){
  10436. if(*q++ != *p++)
  10437. goto mismatch;
  10438. }
  10439.  
  10440. /** A match has been found **/
  10441. /* at position q - patlen */
  10442.  
  10443. nl=q;
  10444.  
  10445. /* look back for the closest newline */
  10446.  
  10447. while(*--nl != '\n')
  10448. ;
  10449. while(*++nl != '\n') putchar(*nl);
  10450. putchar('\n');
  10451.  
  10452. /* we are now at q which is somewhere in the
  10453. * text we reported a match by printing the
  10454. * line, so any possible other matches will
  10455. * NOT have to be searched for in this line
  10456. * so forward to the end of line
  10457. */
  10458. q=nl;
  10459.  
  10460. mismatch:
  10461. /* use the shift that we calculated */
  10462. s += shift;
  10463. }
  10464. memcpy(e, sp, patlen);
  10465. return(0);
  10466. }
  10467.  
  10468. /* End of File */
  10469.  
  10470.  
  10471.  
  10472. Listing 2 The Beaza-Yates and Gonnet algorithm
  10473. /*********************************************
  10474. * Fast String Search, using shift or algorithm
  10475. * July 1993 by Erick Otto.
  10476. * Algorithms from Beaza-Yates and Gonnet
  10477. * are used in this code.
  10478. **********************************************/
  10479.  
  10480. #include <stdio.h>
  10481. #include <fcntl.h)
  10482. #include <ctype.h>
  10483.  
  10484. #define WORD 32 /* # of bits in an Uint */
  10485. #define B 1 /* # of bits to shift */
  10486. #define MAXSYM 256 /* alphabet size */
  10487.  
  10488. unsigned int stopmask;
  10489. unsigned int T[MAXSYM];
  10490.  
  10491. #define TBUF (8*BUFSIZ) /* X times system buffer */
  10492.  
  10493. #define MAXPAT WORD
  10494.  
  10495. main(argc, argv)
  10496. int argc;
  10497. char **argv;
  10498. {
  10499. char buf[TBUF+2];
  10500. char match[MAXPAT];
  10501. register int fd, nread;
  10502. register char *beg, *lastnl, *end;
  10503. char filename[100];
  10504. char first;
  10505. char build();
  10506.  
  10507. /* must be at least two arguments */
  10508. if (argc < 2 ) {
  10509. fprintf(stderr,"\n\tMatch string expected\n");
  10510. exit(1);
  10511. }
  10512. if (argc < 3 ) {
  10513. fprintf(stderr,"\n\tFile expected\n");
  10514. exit(1);
  10515. }
  10516.  
  10517. strcpy(match, argv[1]);
  10518. strcpy(filename, argv[2]);
  10519. first = build(match);
  10520.  
  10521. if ( (fd = open(filename,0_RDONLY)) == -1) {
  10522. fprintf(stderr,
  10523. "Can't open file %s\n",filename);
  10524. perror(filename);
  10525. exit(1);
  10526. }
  10527.  
  10528. *buf = '\n'; /* sentinel for printing purposes */
  10529.  
  10530. beg = buf+1; /* start filling buffer at buf+1 */
  10531.  
  10532. while((nread=read(fd,beg,(&buf[TBUF]-beg))) > 0){
  10533. end = beg+nread;
  10534. lastnl = end;
  10535.  
  10536. while(*--lastnl != '\n')
  10537. ;
  10538.  
  10539. /* lastnl points to the newline */
  10540. matchpat(buf+1, lastnl-buf,first);
  10541.  
  10542. /* move the part skipped (from newline to end of */
  10543. /* buffer) back to the begin of the buffer. */
  10544. memcpy(buf+1, lastnl, end-lastnl-1);
  10545. beg = buf+1 + (end-lastnl);
  10546. }
  10547. close(fd);
  10548. }
  10549.  
  10550. char
  10551. build(pat)
  10552. register char *pat;
  10553. {
  10554. unsigned int mask;
  10555. int i,j;
  10556. char *C_pat;
  10557. char *space;
  10558. char *malloc();
  10559. char startrange;
  10560. char endrange;
  10561. char first;
  10562.  
  10563. first = *pat;
  10564.  
  10565. /* pre process */
  10566.  
  10567. if (first == '.' first == '[') {
  10568. fprintf(stderr,"%s %s\n",
  10569. "Can not start with wildcard \'.\'",
  10570. "or regular expression range");
  10571. exit(1);
  10572. }
  10573.  
  10574. /* Initialize the table */
  10575. for (i=0;i<MAXSYM;i++) {
  10576. T[i] = ~0;
  10577. }
  10578.  
  10579. /* allow for . wildcard */
  10580. space=malloc((strlen(pat)+1) * sizeof(char));
  10581. if (space == NULL) {
  10582. fprintf(stderr,"Malloc failed\n");
  10583. exit(1);
  10584. }
  10585. C_pat = space;
  10586. mask = ~0;
  10587.  
  10588. /* scan the pattern for ranges and or wildcards */
  10589.  
  10590. for(i=1;*pat;i <<= B) {
  10591.  
  10592. switch(*pat) {
  10593.  
  10594. case '[':
  10595. /*start of a range*/
  10596.  
  10597. pat++;
  10598. while(*pat != ']' && *pat) {
  10599.  
  10600. if (isalnum(*pat)) {
  10601.  
  10602. /* if nxt char is a dash it's a range */
  10603.  
  10604. if (*(pat+1) == '-') {
  10605. startrange = *pat;
  10606. pat++;
  10607. pat++;
  10608. endrange = *pat;
  10609. for(j=startrange;j<=endrange;j++){
  10610. T[j] & = ~i;
  10611. }
  10612. *C_pat = startrange;
  10613. } else {
  10614. /*normal character in range*/
  10615. *C_pat = *pat;
  10616. T[*pat] &= ~i;
  10617. }
  10618. } else if (*pat == '-') {
  10619.  
  10620. if (*(pat-1) == '[')
  10621. fprintf(stderr,
  10622. "Invalid Expression\n");
  10623. exit(1);
  10624. } else {
  10625.  
  10626. fprintf(stderr,
  10627. "Invalid Expression\n");
  10628. exit(1);
  10629. }
  10630.  
  10631. pat++;
  10632. } /* while not ']' */
  10633. C_pat++;
  10634. break;
  10635.  
  10636. case '.':
  10637. /* We need to set the char position to something */
  10638. * special, in this case it doesn't really matter */
  10639. *C_pat++ = 'a';
  10640. mask &=~i;
  10641. break;
  10642. default:
  10643. *C_pat++ = *pat;
  10644. break;
  10645. } /* end of switch */
  10646. pat++;
  10647. } /* end of for loop */
  10648.  
  10649.  
  10650. /* close the string and rewind the ptr */
  10651. *C_pat= '\0';
  10652. C_pat = space;
  10653.  
  10654. if (strlen(C_pat) > MAXPAT) {
  10655. fprintf(stderr,
  10656. "Pattern too long max %d chars\n",WORD);
  10657. exit(1);
  10658. }
  10659.  
  10660. /* apply wildcard mask to all elements */
  10661. for (i=0;i<MAXSYM;i++) {
  10662. T[i] =T[i] & mask;
  10663. }
  10664.  
  10665. /* Set masks for all chars that occur in the */
  10666. /* pattern and calculate the match criteria */
  10667.  
  10668. stopmask=0;
  10669. for(j=1;*C_pat;j <<=B) {
  10670. T[*C_pat] &= ~j;
  10671. stopmask =j;
  10672. C_pat++;
  10673. }
  10674.  
  10675. stopmask = ~(stopmask >>B);
  10676. free(space);
  10677. return(first);
  10678. }
  10679.  
  10680. matchpat(text,n,first)
  10681. register char *text;
  10682. int n;
  10683. char first;
  10684. {
  10685.  
  10686. register unsigned int state,initial;
  10687. int matches;
  10688. int gotoeol = 0;
  10689. register char *nl;
  10690. register char *end;
  10691. char *savepos;
  10692. char savechar;
  10693.  
  10694. end = text+n;
  10695.  
  10696. /* save the last character which we use */
  10697. /* as a sentinel for the while loop */
  10698. savepos = end+1;
  10699. savechar = *savepos;
  10700. *savepos = first;
  10701.  
  10702. /* search */
  10703. matches = 0;
  10704. initial = ~0;
  10705.  
  10706. do {
  10707.  
  10708. /* fast scan */
  10709.  
  10710. while(*text != first) text++;
  10711.  
  10712. if (text > end) continue;
  10713.  
  10714. state=initial;
  10715.  
  10716. do {
  10717.  
  10718. state= (state << B) T[*text];
  10719.  
  10720. if (state <stopmask) {
  10721. /****** match **********/
  10722.  
  10723. nl=text;
  10724.  
  10725. /*look back for the closest newline*/
  10726. while(*--nl != '\n')
  10727. ;
  10728.  
  10729. while(*++nl != '\n') putchar(*nl);
  10730. putchar('\n');
  10731.  
  10732. /* skip the rest of the line */
  10733. text=nl;
  10734. }
  10735. text++;
  10736. } while ( state != initial);
  10737. } while (text < end);
  10738.  
  10739. /* reset the character saved */
  10740. *savepos=savechar;
  10741. return(0);
  10742. }
  10743.  
  10744. /* End of File */
  10745.  
  10746.  
  10747. Listing 3 Generates frequency distribution of characters in a file
  10748. /*******************************************
  10749. * Generate a header file with freqency
  10750. * distribution obtained from specified
  10751. * file
  10752. *
  10753. * 1993 by Erick Otto
  10754. ******************************************/
  10755. #include <stdio.h>
  10756.  
  10757. main(argc,argv)
  10758. int argc;
  10759. char *argv[];
  10760. {
  10761.  
  10762. FILE *iptr,*optr;
  10763. int dist[256];
  10764. int c;
  10765. int i;
  10766. int total;
  10767.  
  10768. for (i=0;i<256;i++) dist[i] =0;
  10769.  
  10770. total=0;
  10771.  
  10772. if (argc < 3) {
  10773. fprintf(stderr,"Not enough arguments\n");
  10774. fprintf(stderr,"%s infile outfile\n",argv[0]);
  10775. exit(1);
  10776. }
  10777.  
  10778. if ((iptr = fopen(argv[1],"r")) == NULL) {
  10779. fprintf(stderr,
  10780. "Can not open file %s\n",argv[1]);
  10781. }
  10782. if ((optr = fopen(argv[2],"w")) == NULL) {
  10783. fprintf(stderr,
  10784. "Can not open file %s\n",argv[2]);
  10785. }
  10786.  
  10787. while ((c = getc(iptr)) != EOF) {
  10788. totals++;
  10789. dist[c]++;
  10790. }
  10791.  
  10792. fprintf(optr,"double freq[256] = { /* From %s */\n",
  10793. argv[1]);
  10794.  
  10795. for (i=0;i<256;i++) {
  10796.  
  10797. fprintf(optr,"%G, ",(double)dist[i]/(double)total);
  10798.  
  10799. if (i !=0 && (i % 4) == 0) fprintf(optr,"\n");
  10800. }
  10801. fprintf(optr,"};\n");
  10802. }
  10803.  
  10804. /* End of File */
  10805.  
  10806.  
  10807.  
  10808.  
  10809.  
  10810.  
  10811.  
  10812.  
  10813.  
  10814.  
  10815.  
  10816.  
  10817.  
  10818.  
  10819.  
  10820.  
  10821.  
  10822.  
  10823.  
  10824.  
  10825.  
  10826.  
  10827.  
  10828.  
  10829.  
  10830.  
  10831.  
  10832.  
  10833. Coding with Printable Characters
  10834.  
  10835.  
  10836. Thad Smith
  10837.  
  10838.  
  10839. Thad Smith III is a consultant developing software for embedded measurement,
  10840. control, and communication applications. He is current moderator of the
  10841. Fidonet C Echo and chairman of the localchapter for ACM. He can be reached at
  10842. (303)449-3322 or thadsmith@acm.org.
  10843.  
  10844.  
  10845.  
  10846.  
  10847. Introduction
  10848.  
  10849.  
  10850. I recently started exploring efficient ways to represent a binary stream with
  10851. printable ASCII characters. While high efficiency is not needed for most
  10852. applications, I found it a good exercise in exploring coding techniques. There
  10853. are 95 printable ASCII characters, including space, which can be used, but I
  10854. chose to exclude spaces, since spaces at the beginning or end of a line might
  10855. get lost (or added!). That left 94 characters.
  10856. Ninety-four characters could easily represent six bits (64 values), but seemed
  10857. wasteful. What could I represent with two characters? That would give 94*94 =
  10858. 8,836 combinations, which would nicely hold 13 bits (213 = 8,192) -- a good
  10859. fit. Then I asked the opposite question: how large a character set was needed
  10860. to represent 13 bits in two characters? That would be the square root of
  10861. 8,192, which is just over 90.5. I chose the 91 consecutive ASCII characters
  10862. from ! through {. Listing 1 shows how the 13-bit values were coded into two
  10863. characters. Listing 2 shows how the characters were translated back to the
  10864. original 13 bits.
  10865. The harder part was grouping the input into 13-bit groups when encoding, then
  10866. distributing the 13 bits into eight-bit bytes on output when decoding. The
  10867. efficiency for this conversion is 13/16, since 13 bits are coded into 16 bits
  10868. (assuming eight-bit characters), not counting the overhead of block start,
  10869. block stop, and newlines. Later, someone suggested the challenge of writing an
  10870. efficient encoder program. I knew that the 13/16 conversion program was fairly
  10871. efficient, but wondered if I could do better. I could.
  10872. The most efficient implementation would use all of the combinations of the
  10873. character set, which I restricted to the 94 graphic characters. The amount of
  10874. information in one such character is log2(94) = ln(94)/ln(2) = 6.55458+ bits.
  10875. I was using 8*13/16 = 6.5 bits per encoded character. Using the notation of
  10876. input bits per output bits, the efficiency limit for 94 characters is
  10877. log2(94)/8 = 0.8193236+.
  10878. I decided to use continued fractions to help find a higher encoding ratio.
  10879. Continued fractions are a tool which mathematically suggests an efficient
  10880. rational approximation of any real value. Real values are expressed by a
  10881. series of compounded fractions, as shown by the example in Figure 1. By
  10882. truncating the series at any point, we get a rational approximation to the
  10883. exact value, with more terms offering better approximations. In the case of p,
  10884. we get the series 3/1, 22/7, 333/106, and 355/113 as the first four
  10885. approximations, alternately above and below the true value.
  10886. The expansion of log2(94)/8, our ideal encoding ratio, is shown in Figure 2.
  10887. It expands to the series 0, 1, 4/5, 5/6, 9/11, 59/72, 68/83, ... We must chose
  10888. a ratio from the "low" series 0, 4/5, 9/11, 68/83. I chose to use the ratio
  10889. 9/11 = .81818..., since it was very close to the theoretical value, yet had a
  10890. chance of reasonable implementation.
  10891.  
  10892.  
  10893. Converting the Data
  10894.  
  10895.  
  10896. With a 9/11 encoder, nine eight-bit bytes are converted to an eleven-character
  10897. representation. While that could be done in a straightforward manner, using
  10898. multi-precision arithmetic to work with a 72-bit number, it would require a
  10899. fair amount of code and processing time.
  10900. I chose a method for which most of the input is converted in 32-bit chunks.
  10901. Figure 3 shows a diagram of the way that the bits are redistributed on the way
  10902. from nine bytes to eleven characters. A 32-bit value (from four consecutive
  10903. input bytes) is broken into two parts: one part is converted into a
  10904. four-character value (maximum of 944); the second part, which I call a prefix,
  10905. is in the range 0 to 55 (232/944 = 55.01+). Doing this twice converts eight
  10906. bytes to eight characters plus two values in the range 0 to 55.
  10907. After the two four-byte blocks are converted into eight characters, there are
  10908. three remaining values to encode: two prefixes, in the range 0 to 55, and the
  10909. ninth byte, in the range 0 to 255. Combining them into a single number yields
  10910. 56*56*256 = 802,816 values, which is less than 943 = 830,584, allowing them to
  10911. be converted into three more characters, making eleven altogether. Whew!
  10912.  
  10913.  
  10914. Faster Math
  10915.  
  10916.  
  10917. Initially, the 32-bit value was separated into the prefix value and
  10918. four-character encoded value by dividing by 944 = 78,074,896, where the
  10919. quotient was the prefix and the remainder was the value to be encoded into
  10920. four characters.
  10921. I added a refinement by changing this divisor slightly. Keeping the maximum
  10922. prefix at 55, I could lower the maximum value needed to be encoded into the
  10923. four characters. Doing this, I chose a divisor of 1,171 * 216 (76,742,656),
  10924. with the advantage that the encoder's 32-bit division by 944, which is slow on
  10925. a 16-bit computer, was replaced by a 16-bit division.
  10926. The code in Listing 3 shows the calculation to encode the nine input bytes
  10927. into the eleven output characters. The calculations are done slightly out of
  10928. order, producing the two blocks of four characters first, even though they are
  10929. last in the output buffer (I wanted to keep the output in the same
  10930. significance order as the input).
  10931. Inside the for loop, the 32-bit variable block is first assigned to a value
  10932. from four input bytes. It is then divided into the prefix value q and a
  10933. remainder. The remainder is then further broken into two-character groups,
  10934. which are in turn broken into base-94 digits which are translated to
  10935. characters in the ASCII range ! through ~. The two values of q in the loop are
  10936. combined into qb by a base-56 to binary conversion, resulting in a value 0 to
  10937. 3,135.
  10938. After the loop, qb is combined with the ninth byte (from the first position)
  10939. via shift and OR, then that result is divided into three output characters.
  10940. Since the maximum value of a block on the last calculation is 802,815, it can
  10941. be represented by two base-94 characters plus one base-91 character. The short
  10942. range character is placed first in the 11-character block.
  10943.  
  10944.  
  10945. Encoding and Decoding
  10946.  
  10947.  
  10948. Listing 7 contains the function decode_11_to_9, which shows the code to
  10949. convert from the eleven-character format, dubbed BAZ911, to the nine original
  10950. bytes. It reverses the process of the encoding procedure by converting the
  10951. first three characters into the two prefix codes and the first byte, then
  10952. converts the two blocks of 4 characters, plus associated prefix, into 4 bytes
  10953. each.
  10954. Routines ebaz_data and dbaz_data use the low-level block encode and decode
  10955. routines to encode and decode a data stream of arbitrary length. They are
  10956. called with successive blocks of data, of any size, which are combined and
  10957. converted, with the output being passed off to the output function specified
  10958. in the initialization call. This technique allows the stream encoding and
  10959. decoding to be embedded into various applications.
  10960. These functions also form the encoded output into fixed-length lines (if
  10961. desired), provide/detect a flag for the end of data, and generate/check a CRC
  10962. for data integrity.
  10963. Once a conversion has started, some way is needed to designate the end of
  10964. conversion. EBAZ. C uses an invalid leading character, }, as a flag to
  10965. designate end of conversion. It is followed by a character whose encoded value
  10966. is the number of following bytes, from 0 to 14. The actual number of encoded
  10967. bytes following is two greater, since the 16-bit CRC is included.
  10968. To make the decoding process easier, the minimum size of encoded data is
  10969. eleven characters, allowing a full block of eleven characters of encoded data
  10970. to be assembled before testing for end of data. The encoded output is made
  10971. just long enough to carry the needed bytes only data streams of four bytes or
  10972. less need to be padded with unused information. By retaining at least five
  10973. bytes for use of the next ebaz_data call, the final ensemble, starting with },
  10974. will be at least eleven characters long, thereby simplifying the decoding
  10975. process.
  10976.  
  10977.  
  10978. The Code
  10979.  
  10980.  
  10981. Listing 4 shows BAZ.H, the header for the encoding and decoding functions.
  10982. Listing 5 contains BAZCOM.H, a private header for DBAZ.C and EBAZ.C. Listing 6
  10983. shows EBAZ.C, which contains the stream-level and low-level block encoding.
  10984. Listing 7 shows DBAZ.C, which contain the stream-level and low-level block
  10985. decoding. Listing 8 and Listing 9 show CRC16.H and CRC16G.C, respectively,
  10986. which implement CRC calculations. Finally, Listing 10 and Listing 11 show
  10987. BAZ.C and ZAB.C, respectively, which demonstrate the use of EBAZ and DBAZ to
  10988. encode and decode files. The programs should work in any Standard C
  10989. environment using the ASCII character set and eight-bit characters.
  10990. I donate the code to the public domain, so it can be incorporated into your
  10991. software, including commercial software, without need for licensing.
  10992. Some Printable Encoding Background
  10993. There are several ways to encode binary data into printable form. Some of the
  10994. first I encountered were programs designed to allow an assembler to run on one
  10995. type of machine but produce code for another. Since the type of file storage
  10996. and I/O devices were, in general, unknown, these programs produced the output
  10997. in records, one per line, containing hexadecimal values for the object data.
  10998. Usually, there were other characters, such as S for Motorola's format or : for
  10999. Intel's format, to designate the start of a record, followed by the
  11000. hexadecimal data, each byte being encoded as two ASCII hexadecimal digits.
  11001. These formats became popular and are still in use today for cross assemblers
  11002. and compilers, PROM programmers, emulators, and other tools for cross-platform
  11003. development.
  11004.  
  11005. The efficiency of these formats, using two characters to represent one byte,
  11006. took a back seat to portability. My first such use of a hexadecimal object
  11007. format was in punched cards, read by a system which didn't even use ASCII or
  11008. EBCDIC!
  11009. Later, ASCII become popular and utilities such as UUEncode translated six bits
  11010. of data into a 64-character subset of ASCII. Three eight-bit bytes could be
  11011. broken into four six-bit values, which became four characters by either adding
  11012. the value to the starting ASCII code or by indexing into a lookup table.
  11013. Special character sequences would normally designate the start and end of
  11014. coding, and other information about the file. The output was normally broken
  11015. into lines of 80 characters or less for ease of handling.
  11016. Figure 1
  11017. Figure 2
  11018. Figure 3 Distribution of bits in 9-to-11 encoding process
  11019.  
  11020. Listing 1 Coding 13-bit values into two characters
  11021. #define BASE 91 /* # possible output chars */
  11022. #define FIRST_CODE '!' /* lowest output character */
  11023. #define MAKE_PRINT(c) ((c)+FIRST_CODE)
  11024.  
  11025. put_2_ASCII (n)
  11026. unsigned int n;
  11027. {
  11028. /* put_2_ASCII() converts the 13-bit argument to two
  11029. ** characters and writes them to the output file.
  11030. */
  11031. unsigned int rem;
  11032. rem = n % BASE;
  11033. n = n / BASE;
  11034. putc (MAKE_PRINT(n), outf);
  11035. putc (MAKE_PRINT(rem), outf);
  11036. }
  11037.  
  11038. /* End of File */
  11039.  
  11040.  
  11041. Listing 2 Translating two characters back into 13-bit values
  11042. #define BASE 91 /* # possible output chars */
  11043. #define FIRST_CODE '!' /* lowest output character */
  11044. #define BASEVAL(c) ((c)-FIRST_CODE)
  11045. ...
  11046. int t = getcode(); /* get next valid char */
  11047. int u = getcode(); /* get next valid char */
  11048. return BASEVAL(t) * BASE + BASEVAL(u);
  11049.  
  11050. /* End of File */
  11051.  
  11052.  
  11053. Listing 3 Calculation to encode 9 input bytes into 11 output characters
  11054. #define BASE 94 /* # possible output chars */
  11055. #define FIRST_CODE '!' /* lowest output char */
  11056. #define PBASE 56 /* prefix base */
  11057. #define BASESQ (unsigned long)(BASE*BASE)
  11058. #define MAKE_PRINT(c) (char)((c)+FIRST_CODE)
  11059. #define CV2ASCII(p,v) (*(p)=MAKE_PRINT((v)/BASE), \
  11060. *(p+1)=MAKE_PRINT((v)%BASE))
  11061. #define PBMULT (unsigned)(((0xffffffffUL/PBASE)>>16)+1)
  11062. ...
  11063. /* Encode 9 bytes into 11 printable ASCII chars. */
  11064. unsigned long block; /* conversion area */
  11065. int i; /* input byte index */
  11066. unsigned int qb = 0; /* prefixes */
  11067. ldiv_t ld; /* quotient, remainder */
  11068.  
  11069. for (i = 1; i < 9; i += 4) {
  11070. unsigned q;
  11071. block = ((unsigned long)
  11072. (((unsigned)in[i+0]<<8) Â½ in[i+1])<<16)+
  11073.  
  11074. (((unsigned) in[i+2]<<8) Â½ in[i+3]);
  11075.  
  11076. q = (unsigned) (block >> 16) / PBMULT;
  11077. block = block- ((unsigned long)(q*PBMULT) << 16);
  11078. ld = ldiv ((long) block, (long) BASESQ);
  11079. CV2ASCII(out+i+2, (unsigned) ld.quot);
  11080. CV2ASCII(out+i+4, (unsigned) ld.rem);
  11081. qb = qb * PBASE + q;
  11082. }
  11083.  
  11084. /* Now convert the remaining byte and prefixes
  11085. * from previous block conversions */
  11086. block = ((unsigned long) qb << 8) Â½ in[0];
  11087. ld =ldiv ((long) block, (long) BASESQ);
  11088. out[0] = MAKE_PRINT((unsigned) ld.quot);
  11089. CV2ASCII(out+1, (unsigned) ld.rem);
  11090.  
  11091. /* End of File */
  11092.  
  11093.  
  11094. Listing 4 External definitions of BAZ routines
  11095. /* BAZ.H
  11096. * Contributed to Public Domain 9/93
  11097. * by Thad Smith, Boulder Co.
  11098. */
  11099.  
  11100. /* External interfaces */
  11101. typedef enum { /* decoder return status: */
  11102. DECR_OK, /* normal return */
  11103. DECR_NO_ENDMARK, /* no end marker on data */
  11104. DECR_INVALID_DATA, /* invalid input data */
  11105. DECR_CRC_ERR, /* CRC error */
  11106. DECR_END /* decoding complete */
  11107. } decode_stat;
  11108.  
  11109. /* Output function type */
  11110. typedef int outf_t (const char *out, size_t len);
  11111.  
  11112. int
  11113. ebaz_init ( /* Initialize encoder */
  11114. int p_width, /* width of output lines */
  11115. outf_t * p_outfunc); /* function taking output */
  11116.  
  11117. int
  11118. ebaz_data ( /* Encode data block */
  11119. const unsigned char *data, /* input data */
  11120. size_t len); /* length of data or 0=end */
  11121.  
  11122. int
  11123. dbaz_init ( /* Initialize encoder */
  11124. outf_t * p_outfunc); /* function taking output */
  11125.  
  11126. decode_stat
  11127. dbaz_data ( /* Decode data block */
  11128. const unsigned char *data, /* input data */
  11129. size_t len); /* length of data or 0=end */
  11130.  
  11131. /** Return number of characters in internal buffer.
  11132. * This can be used after DECR_END status is returned
  11133.  
  11134. * to determine the number of unused characters given
  11135. * to the decoder. */
  11136. size_t dbaz_excess_chars (void);
  11137.  
  11138. void
  11139. encode_9_to_11 ( /* Basic block encoder */
  11140. char out[11], const unsigned char in[9]);
  11141. int /* return: 0=OK, 1= invalid input */
  11142. decode_11_to_9 ( /* Basic block decoder */
  11143. unsigned char out [9], const char in[11]);
  11144.  
  11145. /* End of File */
  11146.  
  11147.  
  11148. Listing 5 Internal definitions for BAZ routines
  11149. /* BAZCOM.H
  11150. * Contributed to Public Domain 9/93
  11151. * by Thad Smith, Boulder Co.
  11152. */
  11153.  
  11154. /* Internal constants */
  11155. #define BASE 94 /* # possible output chars */
  11156. #define FIRST_CODE '!' /* lowest output char */
  11157. #define END_FLAG '}' /* starts last data block */
  11158. #define PBASE 56 /* prefix base */
  11159. #define BINB_LEN 9 /* length of binary block */
  11160. #define ENCB_LEN 11 /* length of encoded block */
  11161. #define MAX_ENDBLK_DB 13 /* max # data bytes in
  11162. * final ensemble */
  11163. #define MAX_ENDBLK_LEN (MAX_ENDBLK_DB+2+2*2) /* max
  11164. *length of final ensemble, starting with END_FLAG */
  11165. #define CRC_INIT_VALUE (~0) /* CRC seed */
  11166. /* prefix multiplier */
  11167. #define PBMULT (unsigned)(((0xffffffffUL/PBASE)>>16)+1)
  11168.  
  11169. /* End of File */
  11170.  
  11171.  
  11172. Listing 6 Encode 9 bytes to 11 characters
  11173. /* EBAZ.Cz
  11174. * Contributed to Public Domain 9/93
  11175. * by Thad Smith, Boulder Co.
  11176. */
  11177. #include <stdlib.h>
  11178. #include <string.h>
  11179. #include "baz.h"
  11180. #include "bazcom.h"
  11181. #include "crc16.h"
  11182.  
  11183. static unsigned int crc;/* input CRC */
  11184. static int width; /* # output chars/line */
  11185. static int col; /* current column */
  11186. static int ninb; /* # bytes in inbuf */
  11187. static unsigned char inbuf[BINB_LEN * 2];
  11188. static char outbuf[ENCB_LEN * 2];
  11189. static int (*outfunc) (const char *out, size_t len);
  11190.  
  11191. #define BASESQ (unsigned long)(BASE*BASE)
  11192. #define MAKE_PRINT(c) (char)((c)+FIRST_CODE)
  11193.  
  11194. #define CV2ASCII(p,v) ((p)[0]=MAKE_PRINT((v)/BASE), \
  11195. (p)[1]=MAKE_PRINT((v)%BASE))
  11196.  
  11197. static int putn (const char *out, int n);
  11198.  
  11199. /* Initialize the BAZ911 encoder. */
  11200. int ebaz_init (
  11201. int p_width, /* width of output lines */
  11202. outf_t *p_outfunc
  11203. ){
  11204. initcrctab ();
  11205. crc = CRC_INIT_VALUE;
  11206. ninb = 0;
  11207. col = 0;
  11208. outfunc = p_outfunc;
  11209. width = p_width;
  11210. return 0;
  11211. }
  11212.  
  11213. /* Encode the next block of data. */
  11214. int ebaz_data (
  11215. const unsigned char *data,
  11216. size_t len /* length of data or 0=end */
  11217. ){
  11218. int s; /* output return status */
  11219.  
  11220. if (len) {
  11221. unsigned int cl; /* # bytes needed for block */
  11222. while (len > MAX_ENDBLK_DB - ninb) {
  11223. if (ninb) {
  11224. if (ninb < BINB_LEN) {
  11225. cl = BINB_LEN - ninb;
  11226. memcpy (inbuf + ninb, data, cl);
  11227. len -= cl;
  11228. data += cl;
  11229. ninb = BINB_LEN;
  11230. }
  11231. /* convert block in inbuf */
  11232. crc = updcrc (crc, inbuf, BINB_LEN);
  11233. encode_9_to_11 (outbuf, inbuf);
  11234. if ((s = putn (outbuf, ENCB_LEN)) != 0)
  11235. return s;
  11236.  
  11237. /* Now move remainder in inbuf down */
  11238. memmove (inbuf, inbuf + BINB_LEN,
  11239. ninb -= BINB_LEN);
  11240. } else {
  11241. /* Encode full blocks from input buffer */
  11242. for (; len > MAX_ENDBLK_DB;
  11243. data += BINB_LEN, len -= BINB_LEN) {
  11244.  
  11245. crc = updcrc (crc, data, BINB_LEN);
  11246. encode_9_to_11 (outbuf, data);
  11247. if ((s = putn (outbuf, ENCB_LEN)) !=0)
  11248. return s;
  11249. }
  11250. }
  11251. }
  11252. /* Copy remainder of input to working buffer */
  11253.  
  11254. memcpy (inbuf + ninb, data, len);
  11255. ninb += len;
  11256. return 0;
  11257.  
  11258. } else { /* final block of data */
  11259. /** Write endmarker with final byte count.
  11260. * Insert CRC and fill out blocks with 0xff to
  11261. * prevent change when output is truncated. */
  11262. char endmark[2];
  11263. endmark[0] = END_FLAG;
  11264. endmark[1] = MAKE_PRINT(ninb);
  11265. if ((s = putn (endmark, sizeof endmark)) != 0)
  11266. return s;
  11267.  
  11268. crc = updcrc (crc, inbuf, ninb);
  11269. inbuf[ninb++] = (crc >> 8) & 0xff;
  11270. inbuf[ninb++] = crc & 0xff;
  11271. memset (inbuf + ninb, 0xff, BINB_LEN * 2 - ninb);
  11272. encode_9_to_11 (outbuf, inbuf);
  11273. if (ninb > BINB_LEN) {
  11274. ninb += 2;
  11275. encode_9_to_11 (outbuf + ENCB_LEN,
  11276. inbuf + BINB_LEN);
  11277. } else if (ninb < BINB_LEN-2) ninb = BINB_LEN-2;
  11278. /* Truncate the last block to the number of
  11279. * required characters for the length. */
  11280. if ((s = putn (outbuf, ninb + 2)) != 0)
  11281. return s;
  11282. return outfunc ("\n", 1);
  11283. }
  11284. }
  11285.  
  11286. /* Send output to the output function, adding newline
  11287. * character every width encoded characters. */
  11288. static int
  11289. putn (const char *out, int n)
  11290. {
  11291. int s; /* output return status */
  11292. while (width && col + n > width) {
  11293. if (width > col) {
  11294. if ((s = outfunc (out, width - col)) != 0)
  11295. return s;
  11296. }
  11297. if ((s = outfunc ("\n", 1)) != 0)
  11298. return s;
  11299. n -= (width - col);
  11300. out += (width - col);
  11301. col = 0;
  11302. }
  11303. col +=n;
  11304. return outfunc (out, n);
  11305. }
  11306.  
  11307. /* Encode 9 bytes into 11 printable ASCII chars. */
  11308. void encode_9_to_11 (char out[11],
  11309. const unsigned char in[9])
  11310. {
  11311. unsigned long block; /* conversion area */
  11312. int i; /* input byte index */
  11313.  
  11314. unsigned int qb = 0; /* prefixes */
  11315. ldiv_t ld; /* quotient, remainder */
  11316.  
  11317. for (i = 1; i < 9; i += 4) {
  11318. unsigned q;
  11319. block = ((unsigned long)
  11320. (((unsigned)in[i+0]<<8) in[i+1])<<16)+
  11321. (((unsigned) in[i+2]<<8) in[i+3]);
  11322.  
  11323. q = (unsigned) (block >> 16) / PBMULT;
  11324. block = block- ((unsigned long)(q*PBMULT) << 16);
  11325. ld = ldiv ((long) block, (long) BASESQ);
  11326. CV2ASCII(out+i+2, (unsigned) ld.quot);
  11327. CV2ASCII(out+i+4, (unsigned) ld.rem);
  11328. qb = qb * PBASE + q;
  11329. }
  11330.  
  11331. /* Now convert the remaining byte and prefixes
  11332. * from previous block conversions */
  11333. block = ((unsigned long) qb << 8) in[0];
  11334. ld =ldiv ((long) block, (long) BASESQ);
  11335. out[0] = MAKE_PRINT((unsigned) ld.quot);
  11336. CV2ASCII(out+1, (unsigned) ld.rem);
  11337. }
  11338.  
  11339. /* End of file */
  11340.  
  11341.  
  11342. Listing 7 Decode 9 bytes from 11 characters
  11343. /* DBAZ.C
  11344. * Contributed to Public Domain 9/93
  11345. * by Thad Smith, Boulder Co.
  11346. */
  11347. #include <stdlib.h>
  11348. #include <string.h>
  11349. #include "baz.h"
  11350. #include "bazcom.h"
  11351. #include "crc16.h"
  11352.  
  11353. #define LAST_CODE (FIRST_CODE + BASE -1)
  11354. #defin e BASESQ (unsigned long)(BASE*BASE)
  11355. #define BASEVAL(c) (unsigned)((c)-FIRST_CODE)
  11356. #define ISENCODE(c) ((c) >= FIRST_CODE && \
  11357. (c) <= LAST_CODE)
  11358.  
  11359. static unsigned int crc;/* input file CRC */
  11360. static size_t n_unused; /* # unused input bytes */
  11361. static int ninb; /* # bytes in inbuf */
  11362. static int done; /* set if finished */
  11363. static char inbuf[ENCB_LEN * 2 + 2];
  11364. static unsigned char outbuf[BINB_LEN * 2];
  11365. static int (*outfunc) (const char *out, size_t len);
  11366.  
  11367. static decode_stat
  11368. decode_endmark(const char *data, size_t len);
  11369. /* Initialize the BAZ911 decoder. */
  11370. int dbaz_init (outf_t *p_outfunc) {
  11371. initcrctab ();
  11372. crc = CRC_INIT_VALUE;
  11373.  
  11374. done = 0;
  11375. ninb = 0;
  11376. n_unused = 0;
  11377. outfunc = p_outfunc;
  11378. return 0;
  11379. }
  11380.  
  11381. /* Decode block of data. */
  11382. decode_stat
  11383. dbaz_data (const unsigned char *data, size_t len) {
  11384. int s; /* output return status */
  11385.  
  11386. if (done) {
  11387. n_unused +=len;
  11388. return DECR_END;
  11389. } else if (len == 0)
  11390. return DECR_NO_ENDMARK;
  11391.  
  11392. for (;;) {
  11393. /* fill block input buffer */
  11394. while (ninb < ENCB_LEN) {
  11395. if (ISENCODE(*data))
  11396. inbuf[ninb++]= (char) *data;
  11397. data++;
  11398. if (--len == 0)
  11399. return DEC_OK;
  11400. }
  11401. if (*inbuf == END_FLAG) {
  11402. return decode_endmark ((const char *) data,
  11403. len);
  11404.  
  11405. }
  11406. if (decode_11_to_9 (outbuf, inbuf))
  11407. return DECR_INVALID_DATA;
  11408. ninb = 0;
  11409. crc = updcrc (crc, outbuf, BINB_LEN);
  11410. if ((s= outfunc ((char *)outbuf,BINB_LEN)) != 0)
  11411. return (decode_stat) s;
  11412. }
  11413. }
  11414.  
  11415. /* Return number of characters in internal buffer.
  11416. * This can be used after DECR_END status is returned
  11417. * to determine the number of unused characters given
  11418. * to the decoder. */
  11419. size_t dbaz_excess_chars (void) {
  11420. return n_unused;
  11421. }
  11422.  
  11423. decode_stat decode_endmark (
  11424. const char *data, /* data following inbuf */
  11425. size_t len /* length of data */
  11426. )
  11427. {
  11428. int s; /* output return status */
  11429. int nreq; /* # input bytes required for
  11430. * end ensemble */
  11431. unsigned int rc; /* # remaining data bytes */
  11432.  
  11433.  
  11434. if ((rc = BASEVAL(inbuf[1])) > MAX_ENDBLK_DB)
  11435. return DECR_INVALID_DATA;
  11436.  
  11437. memset (inbuf+ninb, FIRST_CODE, 2*ENCB_LEN+2-ninb);
  11438. nreq = rc +4 +CRCLBY;
  11439. if (nreq < ENCB_LEN)
  11440. nreq = ENCB_LEN; /* min size is 1 block */
  11441. if (rc > BINB_LEN - CRCLBY)
  11442. nreq += 2; /* use part of 2nd blk */
  11443. /* get remaining characters */
  11444. while (ninb < nreq) {
  11445. if (len == 0)
  11446. return DECR_OK; /* need more input chars */
  11447. if (ISENCODE(*data))
  11448. inbuf[ninb++] = *data;
  11449. data++;
  11450. len--;
  11451. }
  11452. done = 1;
  11453. if (decode_11_to_9 (outbuf, inbuf+2))
  11454. return DECR_INVALID_DATA;
  11455. if (rc > BINB_LEN - CRCLBY) {
  11456. if (decode_11_to_9 (outbuf+BINB_LEN,
  11457. inbuf+2+ENCB_LEN))
  11458. return DECR_INVALID_DATA;
  11459. }
  11460. crc = updcrc (crc, outbuf, rc);
  11461. if ((s = outfunc ((char *) outbuf, rc)) != 0)
  11462. return (decode_stat) s;
  11463. if (((crc >> 8) & 0xff) != outbuf[rc] 
  11464. ( crc & 0xff) != outbuf[rc+1] )
  11465. return DECR_CRC_ERR;
  11466. n_unused = len;
  11467. return DECR_END;
  11468. }
  11469.  
  11470. /* Decode 11 printable ASCII characters into 9 bytes */
  11471. int /* return: 0=OK, 1= invalid input */
  11472. decode_11_to_9 (unsigned char out[], const char in[]) {
  11473. unsigned long block;
  11474. unsigned b2, q1, q2, i;
  11475.  
  11476. block = BASEVAL(in[0]) * BASESQ +
  11477. (BASEVAL(in[1]) * BASE + BASEVAL(in[2]));
  11478. out[0] = (unsigned) block & 0xff;
  11479. b2 = (unsigned) (block >> 8);
  11480. q1 = b2 / PBASE;
  11481. q2 = b2 - q1 * PBASE;
  11482. if (q1 >= PBASE) return 1;
  11483.  
  11484. for (i = 1; i < 9; i += 4) {
  11485. block = (BASEVAL(in[i+2]) * BASE +
  11486. BASEVAL(in[i+3])) * BASESQ +
  11487. (BASEVAL(in[i+4]) * BASE + BASEVAL(in[i+5]));
  11488.  
  11489. if (((unsigned)(block >> 16) >= PBMULT) Ã…Ã…
  11490. q1 == PBASE-1 && block >
  11491. (0xffffffffUL -(PBMULT*(PBASE-1UL) << 16))) {
  11492. return 1;
  11493.  
  11494. }
  11495.  
  11496. block += (unsigned long) (q1 * PBMULT) << 16;
  11497. out[i+0] = (unsigned char) (block >> 24);
  11498. out[i+1] = (unsigned char) (block >> 16);
  11499. out[i+2] = (unsigned char) (block >> 8);
  11500. out[i+3] = (unsigned char) block;
  11501. q1 = q2;
  11502. }
  11503. return 0;
  11504. }
  11505.  
  11506. /* End of File */
  11507.  
  11508.  
  11509. Listing 8 Header for 16-bit CRC calculation
  11510. /* CRC16.H
  11511. *
  11512. * Use updcrc() for a block of data,
  11513. * UPDATE_CRC1() for a single byte.
  11514. *
  11515. * Adapted from CRC-16F.C, a public domain routine
  11516. * in Bob Stout's Snippets file collection.
  11517. * Adaptations donated to public domain.
  11518. */
  11519.  
  11520. #define CRCW 16 /* # bits in CRC */
  11521. #define CRCLBY (CRCW/8) /* CRC byte length */
  11522. #define CRCMASK ((1U<<CRCW)-1) /* mask for full CRC */
  11523.  
  11524. extern unsigned short crctab[1 << 8];
  11525.  
  11526. void initcrctab(void); /* Initialize CRC table */
  11527. unsigned short updcrc(unsigned short icrc,
  11528. const unsigned char *icp, unsigned int icnt);
  11529.  
  11530. #define UPDATE_CRC1(c,crc) (((crc)<<8) ^ \
  11531. crctab[(((crc)>>(CRCW-8)) ^ (c)) & 0xff])
  11532.  
  11533. /* End of File */
  11534.  
  11535.  
  11536. Listing 9 Calculate the CRC, a block of data at a time
  11537. /** CRC16G. C
  11538. *
  11539. * Adapted from CRC-16F.C, a public domain routine
  11540. * in Bob Stout's Snippets file collection.
  11541. * Adaptations donated to public domain.
  11542. *
  11543. * Call initcrctab() to initialize table.
  11544. */
  11545. #include "crc16.h"
  11546.  
  11547. unsigned short crctab[1 << 8];
  11548. static int initialized = 0;
  11549.  
  11550. #define P 0x1021 /* CRC polynomial */
  11551.  
  11552. unsigned short
  11553.  
  11554. updcrc(unsigned short crc, /* prev CRC */
  11555. const unsigned char *cp, /* new data */
  11556. unsigned int cnt) /* # bytes */
  11557. {
  11558. while (cnt--)
  11559. crc = UPDATE_CRC1(*cp++, crc);
  11560. return (crc & CRCMASK);
  11561. }
  11562.  
  11563. void initcrctab(void) {
  11564. unsigned int b, v;
  11565. int i;
  11566. if (initialized)
  11567. return;
  11568.  
  11569. for (b = 0; b <= (1 << 8) - 1; ++b) {
  11570. for (v = b << (CRCW - 8), i = 8; --i >= 0;)
  11571. v = v & 0x8000 ? (v << 1) ^ P : v << 1;
  11572. crctab[b] = v;
  11573. }
  11574. initialized = 1;
  11575. }
  11576.  
  11577. /* End of File */
  11578.  
  11579.  
  11580. Listing 10 Encode binary file to BAZ911 file
  11581. /* BAZ.C
  11582. * Contributed to Public Domain 9/93
  11583. * by Thad Smith, Boulder Co.
  11584. */
  11585. #include <stdio.h>
  11586. #include <stdlib.h>
  11587. #include "baz.h"
  11588.  
  11589. #define MAX_COL 70 /* # output chars / line */
  11590.  
  11591. FILE *inf, *outf;
  11592. unsigned char buf[8192];
  11593.  
  11594. /* Write data block to output file. */
  11595. /* ret value: 0 = OK, -1 = error */
  11596. int
  11597. enc_out (const char *out, size_t len)
  11598. {
  11599. fwrite ((const void *) out, 1, len, outf);
  11600. return (ferror (outf) ? -1 : 0);
  11601. }
  11602.  
  11603. int
  11604. main (int argc, char *argv[])
  11605. {
  11606. unsigned int n; /* # bytes read */
  11607. int s; /* return output status */
  11608.  
  11609. if (argc != 3) {
  11610. puts ("BAZ - Convert binary file to"
  11611. "BAZ911-encoded file");
  11612. puts ("Usage: BAZ infile outfile");
  11613.  
  11614. puts (" infile = input file (any format)");
  11615. puts (" outfile= output file in BAZ911 format");
  11616. return EXIT_FAILURE;
  11617. }
  11618. if ((inf= fopen (argv[1], "rb")) == NULL) {
  11619. fprintf (stderr, "Error opening input file: %s",
  11620. argv[1]);
  11621. return EXIT_FAILURE;
  11622. }
  11623. if ((outf = fopen (argv[2], "w")) == NULL) {
  11624. fprintf (stderr, "Error opening output file: %s",
  11625. argv[2]);
  11626. return EXIT_FAILURE;
  11627. }
  11628. fprintf (outf, "BAZ911: %s\n", argv[1]);
  11629. ebaz_init (MAX_COL, enc_out);
  11630. do {
  11631. n = fread (buf, 1, sizeof buf, inf);
  11632. s = ebaz_data (buf, n);
  11633. } while (n > 0 && s == 0);
  11634. putc ('\n', outf);
  11635. if (ferror (outf)) {
  11636. fprintf (stderr, "Error writing output file\n");
  11637. return EXIT_FAILURE;
  11638. }
  11639. if (ferror (inf)) {
  11640. fprintf (stderr, "Error reading input file\n");
  11641. return EXIT_FAILURE;
  11642. }
  11643. return EXIT_SUCCESS;
  11644. }
  11645.  
  11646. /* End of File */
  11647.  
  11648.  
  11649. Listing 11 Decode BAZ911-encoded file
  11650. /* ZAB.C
  11651. * Contributed to Public Domain 9/93
  11652. * by Thad Smith, Boulder Co.
  11653. */
  11654. #include <stdio.h>
  11655. #include <stdlib.h>
  11656. #include <string.h>
  11657. #include "baz.h"
  11658.  
  11659. FILE *inf, *outf; /* input, output files */
  11660.  
  11661. char *error_msg[] = {
  11662. "", "End of file before end of data\n",
  11663. "Invalid input data\n", "CRC error\n",
  11664. "Conversion complete\n"};
  11665.  
  11666. unsigned char buf[8192];
  11667.  
  11668. /* Write data block to output file. */
  11669. /* ret value: 0 = OK, -1 = error */
  11670. int dec_out (const char *out, size_t len) {
  11671. fwrite ((const void *) out, 1, len, outf);
  11672. return (ferror (outf) ? -1 : 0);
  11673.  
  11674. }
  11675.  
  11676. int main (int argc, char *argv[]) {
  11677.  
  11678. char line[100]; /* line buffer for header
  11679. * search */
  11680. unsigned int n; /* number of bytes read */
  11681. decode_stat s; /* return status from
  11682. * encode/write */
  11683.  
  11684. if (argc!= 3) {
  11685. puts ("ZAB - Convert BAZ911-encoded file to"
  11686. "binary file");
  11687. puts ("Usage: ZAB infile outfile");
  11688. puts (" infile= name of file in BAZ911 "
  11689. "format");
  11690. puts (" outfile= output binary file");
  11691. return EXIT_FAILURE;
  11692. }
  11693. if ((inf = fopen (argv[1], "r")) == NULL) {
  11694. fprintf (stderr, "Error opening input file: %s",
  11695. argv[1]);
  11696. return EXIT_FAILURE;
  11697. }
  11698. if ((outf = fopen (argv[2], "wb")) == NULL) {
  11699. fprintf (stderr,"Error opening output file: %s",
  11700. argv[2]);
  11701. return EXIT_FAILURE;
  11702. }
  11703. dbaz_init (dec_out);
  11704. do {
  11705. if (!fgets (line, sizeof line, inf)) {
  11706. fprintf (stderr, "BAZ911 starting flag not "
  11707. "found in %s\n", argv[1]);
  11708. return EXIT_FAILURE;
  11709. }
  11710. }while (strncmp (line, "BAZ911:", 7) != 0);
  11711. do {
  11712. n = fread (buf, 1, sizeof buf, inf);
  11713. s = dbaz_data (buf, n);
  11714. if (s != DECR_OK) {
  11715. if (s == DECR_END)
  11716. break;
  11717. if (s == -1)
  11718. fprintf (stderr, "Error writing file\n");
  11719. else
  11720. fprintf (stderr, error_msg[s]);
  11721. return EXIT_FAILURE;
  11722. }
  11723. } while (n > 0);
  11724. if (ferror (inf)) {
  11725. fprintf (stderr, "Error reading input file\n");
  11726. return EXIT_FAILURE;
  11727. }
  11728. return EXIT_SUCCESS;
  11729. }
  11730.  
  11731. /* End of File */
  11732.  
  11733.  
  11734.  
  11735.  
  11736.  
  11737.  
  11738.  
  11739.  
  11740.  
  11741.  
  11742.  
  11743.  
  11744.  
  11745.  
  11746.  
  11747.  
  11748.  
  11749.  
  11750.  
  11751.  
  11752.  
  11753.  
  11754.  
  11755.  
  11756.  
  11757.  
  11758.  
  11759.  
  11760.  
  11761.  
  11762.  
  11763.  
  11764.  
  11765.  
  11766.  
  11767.  
  11768.  
  11769.  
  11770.  
  11771.  
  11772.  
  11773.  
  11774.  
  11775.  
  11776.  
  11777.  
  11778.  
  11779.  
  11780.  
  11781.  
  11782.  
  11783.  
  11784.  
  11785.  
  11786.  
  11787.  
  11788.  
  11789.  
  11790.  
  11791.  
  11792.  
  11793.  
  11794.  
  11795.  
  11796.  
  11797. Intuitive Access to Bit Arrays
  11798.  
  11799.  
  11800. Siegfried Heintze
  11801.  
  11802.  
  11803. Siegfried Heintze is a Software Engineer involved in research, software
  11804. development and training. He consults in all areas of software engineering and
  11805. specializes in object oriented techniques. He has developed and taught courses
  11806. on object oriented design and analysis and object oriented programming.
  11807.  
  11808.  
  11809.  
  11810.  
  11811. Introduction
  11812.  
  11813.  
  11814. Our ability to express our ideas is often constrained by the syntax and the
  11815. semantics of a programming language. The difference between what we want to
  11816. write and what we are constrained to write is spoken of as the "semantic gap."
  11817. The better a language is able to support abstraction, the less prominent is
  11818. the semantic gap. Facilities to support abstraction include abstract data
  11819. types and object-oriented programming.
  11820. This article identifies some semantic gaps in the C programming language and
  11821. discusses bridging the semantic gap using C++. I had always regretted the fact
  11822. the C programming language had no explicit support for values sometimes best
  11823. represented as single bits, such as TRUE or FALSE. C programmers often use
  11824. typedef unsigned char Boolean;
  11825. as a substitute when space is not an issue. If, however, it is evident that
  11826. packing our bits into an unsigned long is appropriate, then we have to use
  11827. macros. So, when we wanted to say:
  11828. BitArray x, b;
  11829. x[5] = b[6];
  11830. we were constrained to using some macros that might make the above statements
  11831. look like this in C:
  11832. typedef unsigned long BitArray;
  11833. BitArray x, b;
  11834. int t;
  11835. t = bitx(b,6);
  11836. /* extract bit position 6, length 1 */
  11837. bitd(x,5,t);
  11838. /* deposit into bit position 5, length 1 */
  11839.  
  11840.  
  11841. Class BitArray
  11842.  
  11843.  
  11844. As you can see, there is quite a difference between what we wanted to write
  11845. (or could write if we were writing in a language such as Pascal) and what the
  11846. compiler constrained us to write. By incorporating the features of C++ we can
  11847. overload the subscript operator (operator[]) to do what ever we want --
  11848. correct? Well maybe. Even C++ places some constraints on what we do. Let's
  11849. start with a naive (but functional) implementation of class BitArray.
  11850. class BitArray {
  11851. public:
  11852. BitArray();
  11853. int operator[](int);
  11854. void set(int value, int pos);
  11855. private:
  11856. unsigned long _data;
  11857. };
  11858. With this class definition, we can now write:
  11859. BitArray x, b;
  11860. x.set(b[6],5);
  11861. This is an improvement over the previous fragment in C because we can use
  11862. operator[] to extract a bit. Unfortunately, we cannot use operator[] and
  11863. operator= (the assignment operator) to deposit a value into an array of bits.
  11864. As a substitute, we introduce the member function set to deposit new values
  11865. into the array.
  11866. If we want to abandon member function set and allow operator[] to be a
  11867. modifiable 1value (an expression that can receive a value by being placed on
  11868. the left hand side of an assignment statement), we typically overload
  11869. operator[] to return a reference to an element of the array. Then we let the
  11870. operator= actually perform the transfer of the value. We typically see
  11871. operator[] overloaded as follows:
  11872. class IntArray {
  11873. public:
  11874. int& operator[](int sub){
  11875. {...}
  11876. ...
  11877. };
  11878. The logic here is that we use a single function called operator[] to both
  11879. deposit and extract values from the container object. The & indicates that we
  11880. return a reference to a value instead of a value. Returning a reference
  11881. permits operator[] to appear on the left side of an assignment statement, as
  11882. in:
  11883. IntArray x, b; x[6] = b[5];
  11884. There are two problems with this approach, however:
  11885. 1) We cannot return a reference to a single bit.
  11886.  
  11887. 2) The function for fetching an element out of the array is identical to the
  11888. function for storing an element. (In both cases we merely return a reference
  11889. to the element).
  11890. These constraints are clearly not a problem when implementing an array of int
  11891. because our function return type is a reference to an int. The problem, then,
  11892. is how to access (fetch and deposit) the elements of an array of bits using
  11893. operator[] and operator=.
  11894. The focus of the remainder of this article will be to examine some important
  11895. features of the C++ language and develop a technique to solve the problem of
  11896. returning a reference to a bit. Later, we will note that this is not the only
  11897. place where type constraints make the naive use of operator[] difficult, so we
  11898. can apply this technique elsewhere.
  11899.  
  11900.  
  11901. const Function Arguments
  11902.  
  11903.  
  11904. The const type qualifier allows us to tell the compiler that we promise not to
  11905. modify an object. The object might be a function value, a function argument,
  11906. or the secret (or implied) argument of a member function. This is done with a
  11907. const type qualifier that becomes part of the signature of the function.
  11908. Consider the following fragment of code:
  11909. void func(const BitArray & x)
  11910. {
  11911. if (x[0] == 1)
  11912. ...
  11913. }
  11914. Here we are passing the argument by reference (which sometimes indicates that
  11915. we plan to modify the argument). But then we declare the argument to be const,
  11916. which indicates that the body of code is not permitted to do so. This may seem
  11917. confusing. Why did not we just pass the argument by value (the default passing
  11918. mechanism in C and C++ that automatically makes a copy of the object being
  11919. passed)? Often the answer is that we could have, but passing by reference is
  11920. more efficient because it is not necessary to invoke the constructor and
  11921. destructor for the parameter x.
  11922. Sometimes, in copy constructors for example, we are not permitted to pass by
  11923. value. The problem with the above fragment of code is that the compiler will
  11924. complain that we are modifying the object x. This is not actually true, of
  11925. course, we are just examining it. Nevertheless, we can observe that we will
  11926. sometimes be using operator[] to modify a BitArray object and other times we
  11927. will need to explicitly state that operator[] will not modify the object. We
  11928. can use the const feature to overload operator[]. We now modify the definition
  11929. of class BitArray:
  11930. class BitArray {
  11931. public:
  11932. int& operator[](int sub)
  11933. {...}
  11934. int operator[](int sub) const
  11935. {...}
  11936. ...
  11937. };
  11938. The const in the second function is part of the signature and consequently
  11939. becomes a criterion for selecting the correct function among several
  11940. overloaded functions of the same name. We have solved one problem: we can now
  11941. use operator[] for read-only access inside functions that don't permit us to
  11942. modify BitArray objects. However, we are not finished yet because we don't
  11943. know what the value is that we are going to deposit! Recall that operator[]
  11944. has only two arguments: the implied argument (which is the array of bits) and
  11945. the subscript.
  11946.  
  11947.  
  11948. Class BitField
  11949.  
  11950.  
  11951. Given the code:
  11952. BitArray x, b;
  11953. x[6] = b[5];
  11954. How does the operator[] function on the left know that we want to deposit a
  11955. value of one and not of some other value, perhaps zero? The answer is that it
  11956. cannot. Only the operator= function knows this. In the last code fragment,
  11957. operator= receives two integer values. But the operator= function needs to
  11958. know:
  11959. 1) the address of the representation of the array object (here the address of
  11960. an unsigned long)
  11961. 2) the subscript
  11962. 3) the length of the bit field (in this example it is assumed to be one)
  11963. 4) the value to be deposited in the array of bits.
  11964. The problem, then, is to communicate these four pieces of information to the
  11965. operator= function.
  11966. To communicate the first three pieces of information we introduce a new class
  11967. whose declaration looks something like this:
  11968. class BitField {
  11969. public:
  11970. BitField(unsigned long *data,
  11971. unsigned int pos,
  11972. unsigned width);
  11973. void operator=(unsigned long rhs);
  11974. private:
  11975. unsigned long *_data;
  11976. int _pos;
  11977. int _width;
  11978. };
  11979. We also change BitArray to accommodate this new change:
  11980. class BitArray {
  11981. public:
  11982. BitField operator[](int) {
  11983. {...}
  11984. int operator[](int) const {
  11985. {...}
  11986. ...
  11987.  
  11988. };
  11989. Now we have solved another problem. We have a function BitField::operator=
  11990. which will be called by the compiler on our behalf when the following code is
  11991. encountered:
  11992. BitArray x, b;
  11993. x[6] = b[5];
  11994. BitField::operator= has access to all four of the necessary pieces of
  11995. information to perform the deposit into the array of bits. When the compiler
  11996. sees x[6] it notes that it is a modifiable lvalue and consequently calls
  11997. function BitField::operator[](int), which returns a BitField object.
  11998. The compiler then looks for an appropriate assignment function and notes that
  11999. class BitField has such a member function. This is then used to perform the
  12000. actual masking to deposit the new value into the unsigned long which holds the
  12001. actual representation of the array of bits. We might consider ourselves
  12002. finished at this point. However, there is still another potential problem. If
  12003. we code in the tradition of many C programmers, we may want to use a feature
  12004. whereby we are allowed to have multiple assignments within a single statement,
  12005. like the following:
  12006. BitArray x;
  12007. x[3] = x[2] = x[1] = x[0] = 1;
  12008. This presents a problem because we declared our assignment operator function
  12009. to be of type void, which means it is really a procedure that has no return
  12010. value. So after we assign the value one to x[0] we have no value to assign to
  12011. x[1].
  12012. How can we make our new class BitField compatible with other built-in types in
  12013. C? The key to this problem is to consider something counterintuitive. We
  12014. normally don't think of operator= as a function that returns a value. But this
  12015. exactly why we are permitted to write expressions such as a = b = c in C and
  12016. C++. We now have a definition of BitField that looks like this:
  12017. class BitField {
  12018. public:
  12019. BitField(unsigned long *data,
  12020. unsigned int pos,
  12021. unsigned int width);
  12022. BitField& operator=
  12023. (unsigned long rhs);
  12024. private:
  12025. unsigned long *_data;
  12026. int _pos;
  12027. int _width;
  12028. };
  12029. We could have declared the assignment operator as:
  12030. BitField operator=(unsigned long)
  12031. which would have returned a value of type BitField instead of a reference to a
  12032. BitField. This would have caused constructors and destructors for the BitField
  12033. class to execute. Since C++ allows us to return references as function values
  12034. we added the & for efficiency at no cost. We still have not solved our problem
  12035. however. operator= only accepts values of type unsigned long. Recall the
  12036. fragment of code causing the problem:
  12037. BitArray x;
  12038. x[3] = x[2] = x[1] = x[0] = 1;
  12039. While we now have the rightmost operator= returning a reference for use by the
  12040. next rightmost operator=, we need a value of type unsigned long when in fact
  12041. we have a reference to an object of type BitField. There are a couple of ways
  12042. to resolve this problem. We shall now examine one of them.
  12043.  
  12044.  
  12045. Conversion Operators
  12046.  
  12047.  
  12048. C++ has yet another feature called conversion operators, which convert from a
  12049. user-defined type to a different type (often a built-in type). We may define
  12050. such a function as:
  12051. class BitField {
  12052. public:
  12053. operator unsigned long() {
  12054. {...}
  12055. };
  12056. Since this is a member function, we have a single implied argument and a
  12057. function return value of type unsigned long. The compiler automatically
  12058. inserts calls to this function to perform the conversions we need for
  12059. statements containing multiple assignments. We now reexamine the example of
  12060. multiple assignment operators one last time:
  12061. BitArray x;
  12062. x[3] = x[2] = x[1] = x[0] = 1;
  12063. Progressing from right to left: The rightmost operator[] returns a reference
  12064. to a BitField object for which its member function operator[] is invoked. The
  12065. function returns a reference to a BitField object. This is a problem because
  12066. the next operator[] wants an integer value, not a reference to a BitField
  12067. object. The conflict is resolved by invoking the coversion operator which
  12068. converts the reference to a BitField object to an integer value which is then
  12069. used by the next operator[].
  12070. There is one scenario in which the conversion operator does not work as we
  12071. would like. Consider some arbitrary constructor that accepts an integer, and a
  12072. function that accepts a const argument of that type:
  12073. struct X{
  12074. X(unsigned int x)
  12075. {cout << "Create X ="
  12076. << x << endl; }
  12077. };
  12078. void f(const X& x)
  12079. {cout << "Function f"; }
  12080. We expect the conversion operator for class X to perform the conversion, and
  12081. indeed it does when the X object is declared to be const. Unfortunately, the
  12082. conversion will not accomodate the conversion to a constant object. The
  12083. workaround, shown below, is to explicitly declare a temporary constant object
  12084. to pass to the function.
  12085. main(){
  12086. X a;
  12087. f(a[1]); //error
  12088. const X temp = a;
  12089. f(temp[1]); //OK
  12090. }
  12091.  
  12092.  
  12093.  
  12094. Other Applications
  12095.  
  12096.  
  12097. Stepping back a bit, we had a scenario where it was intuitive to use the
  12098. subscript notation (operator[]) to access elements of a container object. The
  12099. problem was that, unlike arrays of integers, the method for depositing into
  12100. the object was quite different from extracting a value. Where else might this
  12101. technique be useful?
  12102. The most obvious extension is to accomodate array elements represented by
  12103. multiple bits. We can specify the number of bits to be used for each element
  12104. in the constructor for BitArray. Bitfield objects will then contain the
  12105. address, bit position, and length (i.e., number of bits). This is demonstrated
  12106. in Listing 1, showing the file bitarr.h. The first argument to the constructor
  12107. gives the width in bits. Listing 2 shows the accompanying file bitarr.cpp.
  12108. Interested readers will want to modify this code to accomodate arrays whose
  12109. representation requires more than a single long word of bits. We have a
  12110. similar situation for a video display composed of pixels, where the plot and
  12111. readPixel functions mentioned earlier are used for writing and reading pixels
  12112. respectively. If we have previously programmed in Pascal, we might be tempted
  12113. to pursue implementing a syntax to accommodate code like this:
  12114. VideoDisplay v;
  12115. v[5,6] = v[20,10];
  12116. While this was possible in some older C++ compilers, it is not permitted in
  12117. the current C++ language, because the underlying C language does not permit
  12118. multiple arguments for operator[]. This is unfortunate, but there is a
  12119. reasonable substitute:
  12120. VideoDisplay v;
  12121. v(5,6) = v(20,10);
  12122. We can use the same technique of overloading operator[] twice, the second time
  12123. with a const type qualifier, then defining a temporary class of objects (call
  12124. it class Pixel for this example) for which the appropriate conversion
  12125. operators and assignment operators are defined. Yet another application of
  12126. this technique might be ISAM (indexed sequential access method) files to
  12127. implement persistent objects. We may find it intuitive to access indexed
  12128. records with the subscript or brackets notation when directly using separate
  12129. functions to read and write indexed records.
  12130.  
  12131. Listing 1 Definition of classes BitField and BitArray
  12132. //file: BitArr.h copyright 1994 by Siegfried Heintze
  12133. #ifndef BITARR_H
  12134. #define BITARR_H
  12135. #include "Boolean.h"
  12136. #include <assert.h>
  12137. #include <iostream.h>
  12138.  
  12139. // Class of temporary objects to reference bit fields
  12140. class BitField{
  12141. public:
  12142. BitField(unsigned long *data, unsigned int pos,
  12143. unsigned width):
  12144. _data(data), _pos(pos), _width(width)
  12145. {
  12146. assert(_pos+_width<=32);
  12147. }
  12148. operator unsigned long(){
  12149. unsigned long mask = 0xffffffff >> (32 - _width);
  12150. unsigned long result = (*_data >> _pos) & mask;
  12151. return result;
  12152. }
  12153.  
  12154. //Let the compiler supply these as necessary:
  12155. // BitField(const BitField& src);
  12156. // BitField& operator=(const BitField& rhs);
  12157. // ~BitField();
  12158.  
  12159. // Assignment operator. This is the code that deposits
  12160. // an integer value into the packed array of bits.
  12161. BitField& operator=(unsigned long rhs){
  12162. unsigned long mask = 0xffffffff >> (32 - _width);
  12163. rhs &= mask;
  12164. rhs <<= _pos;
  12165. mask <<= _pos;
  12166. *_data &= ~mask;
  12167. *_data Â½= rhs;
  12168. return *this;
  12169. }
  12170. private:
  12171. unsigned long *_data;
  12172. int _pos;
  12173. int _width;
  12174. };
  12175.  
  12176.  
  12177. class BitArray{
  12178. public:
  12179. // Default Constructor.
  12180. // Optional arguments:
  12181. // width: number of bits in an array element
  12182. // val: initial value of representation of bit array
  12183. BitArray(unsigned int width=1, unsigned long val=0):
  12184. _len(32), _data(val),_width(width){
  12185. assert(_width <= _len);
  12186. }
  12187. //Let the compiler supply these as necessary:
  12188. // BitArray(const BitArray& src);
  12189. // BitArray& operator=(const BitArray& rhs);
  12190. // ~BitArray();
  12191.  
  12192. // Return the number of bits in an array element
  12193. unsigned int width() const {
  12194. return _width;
  12195. }
  12196.  
  12197. // Return the index of the first element in the array
  12198. unsigned int first() const {
  12199. return 0;
  12200. }
  12201.  
  12202. // Return the index of the last element in the array
  12203. unsigned int last() const {
  12204. return _len/_width-1;
  12205. }
  12206.  
  12207. // Return the number of bits in the array
  12208. unsigned int length()const{
  12209. return _len;
  12210. }
  12211.  
  12212. // Fetch an array element
  12213. unsigned long operator[](unsigned int s) const {
  12214. s*=_width;
  12215. assert(s+_width<=_len);
  12216. unsigned long mask = 0xffffffff >> (_len - _width);
  12217. unsigned long result = _data;
  12218. result >>= s;
  12219. result &= mask;
  12220. return result;
  12221. }
  12222.  
  12223. // Possibly deposit a value into an array element.
  12224. // Defer actual deposit operation to BitField::operator=
  12225. BitField operator[](unsigned int s) {
  12226. return BitField(&_data, s*_width, _width);
  12227. }
  12228.  
  12229. private:
  12230. unsigned long _data;
  12231. // Actual representation of BitArray
  12232. unsigned int _len;
  12233. // number of bits in representation
  12234. unsigned int _width;
  12235. // number of bits in a single array element
  12236.  
  12237. };
  12238.  
  12239. #endif
  12240.  
  12241. /* End of File */
  12242.  
  12243.  
  12244. Listing 2 Accompanies file bitarr.h
  12245. //file: BitArr.cpp copyright 1994 by Siegfried Heintze
  12246. #include "BitArr.h"
  12247.  
  12248. ostream& operator<<(ostream& os, const BitArray & bv){
  12249. for(int ii = bv.first(); ii<=bv.last(); ii++){
  12250. os << bv[ii] << " ";
  12251. }
  12252. return os;
  12253. }
  12254.  
  12255. BitArray reverse(const BitArray& src){
  12256. BitArray result(src);
  12257. for(int ii = src.first(); ii<= src.last(); ii++)
  12258. result[src.last()-ii] = src[ii];
  12259. return result;
  12260. }
  12261.  
  12262. struct X{
  12263. X(unsigned long x){
  12264. cout<<"create X, x = "<<hex<<x;
  12265. }
  12266. };
  12267. void f(const X&){
  12268. cout<<" Fuction f"<<endl;
  12269. }
  12270.  
  12271. main(){
  12272. cout << "begin"<<endl;
  12273.  
  12274. BitArray x(1, 0x8aaa71b5ul);
  12275. cout << reverse(x) << endl;
  12276. x[3] = x[2] = x[1] = 1;
  12277. cout << reverse(x) <<endl;
  12278.  
  12279. const BitArray xx = x;
  12280. f(xx[1]);
  12281.  
  12282. BitArray y(4, 0x8aaa71b5ul);
  12283. cout << reverse(y) << endl;
  12284. y[7]=13
  12285. y[1]=15;
  12286. cout << reverse(y) << endl;
  12287.  
  12288. BitArray z(16, 0x8aaa71b5ul);
  12289. cout << reverse(z) << endl;
  12290. z[1]=15;
  12291. cout << reverse(z) << endl;
  12292.  
  12293. return 0; }
  12294.  
  12295. // End of File
  12296.  
  12297.  
  12298.  
  12299.  
  12300.  
  12301.  
  12302.  
  12303.  
  12304.  
  12305.  
  12306.  
  12307.  
  12308.  
  12309.  
  12310.  
  12311.  
  12312.  
  12313.  
  12314.  
  12315.  
  12316.  
  12317.  
  12318.  
  12319.  
  12320.  
  12321.  
  12322.  
  12323.  
  12324.  
  12325.  
  12326.  
  12327.  
  12328.  
  12329.  
  12330.  
  12331.  
  12332.  
  12333.  
  12334.  
  12335.  
  12336.  
  12337.  
  12338.  
  12339.  
  12340.  
  12341.  
  12342.  
  12343.  
  12344.  
  12345.  
  12346.  
  12347.  
  12348.  
  12349.  
  12350.  
  12351.  
  12352.  
  12353.  
  12354.  
  12355.  
  12356.  
  12357.  
  12358.  
  12359.  
  12360. A Self-Extracting Archive for MS-DOS
  12361.  
  12362.  
  12363. P.J. LaBrocca
  12364.  
  12365.  
  12366. Pat LaBrocca is the author of ReCalc(TM), a set of rational expression
  12367. calculators that never give answers (well, almost never), and run identically
  12368. on PCs, Macintoshes and Apples. He has a BS and MA in Chemistry and teaches
  12369. computer science at Peter Rouget Middle School 88 in Brooklyn, NY. You can
  12370. contact him at plabrocc@nycenet.edu.
  12371.  
  12372.  
  12373.  
  12374.  
  12375. Introduction
  12376.  
  12377.  
  12378. An archive is a file that contains several other files. A self-extracting
  12379. archive (SEA) is a file that doesn't need the original archiving software to
  12380. release its contents. In this article I present pair of utilities for creating
  12381. SEAs under MS-DOS. The utilities provide a somewhat barebones implementation
  12382. in that they don't provide features common to other archives, such as
  12383. compression, or preservation of subdirectories. Once you understand how these
  12384. utilities work, you may want to extend them. I provide some references at the
  12385. end of this article for those wishing to extend the utilities.
  12386.  
  12387.  
  12388. The SEA Structure
  12389.  
  12390.  
  12391. My self extracting archive consists of three parts. The first part is the set
  12392. of files that have been archived, which I refer to as the component files. The
  12393. second part, the extraction module, is the code embedded in the archive that
  12394. reads the component files from the archive and writes them to new output
  12395. files. Finally, a system of headers embedded in the archive tell the
  12396. extraction module the lengths and names of the component files. The extraction
  12397. module uses the information in the prefix headers to recreate the files. To
  12398. avoid confusing C's include header files and MS-DOS's program file headers, I
  12399. refer to my headers as prefix headers, since they prefix each component file.
  12400. Also, when I refer to the "archiver" I am speaking of the utility that builds
  12401. the archive. When I refer to the "SEA" I am speaking of the archive itself.
  12402. The disk layout of a SEA containing n component files is shown in Figure 1.
  12403. A terminating suffix header follows the last component file. The suffix header
  12404. indicates to the extraction module that there are no more component files.
  12405.  
  12406.  
  12407. Overview of SEA Process
  12408.  
  12409.  
  12410. The archiver starts by copying the extraction module to the archive. Then for
  12411. each component file, the archiver constructs a prefix header and writes the
  12412. header and the associated file to the archive. When the archiver runs out of
  12413. files to archive, it writes the suffix header.
  12414. The SEA appears to MS-DOS as an .EXE file and executes like any other .EXE
  12415. file. The SEA is actually an .EXE file with extra information appended to the
  12416. end. The appended information does not interfere with the SEA's execution.
  12417. (See the sidebar, "Piggybacking an .EXE File.")
  12418. When you run the SEA, the extraction module reads the prefix headers and
  12419. recreates each of the files in turn. This process continues until the SEA
  12420. reads the suffix header, at which time the extraction is complete.
  12421.  
  12422.  
  12423. The Archiver -- Detailed Operation
  12424.  
  12425.  
  12426. The source code for the archiver is in arch.c (Listing 2). The extraction
  12427. module and the archiver expect the same prefix headers, so I put the prefix
  12428. header structure in a separate include file (see Listing 1). After verifying
  12429. that the user has provided a list of files to archive, the archiver begins
  12430. opening files. First, arch.exe tries to open the extraction module, extr.exe,
  12431. to prepare to copy it to the archive. If arch.exe can't find the extraction
  12432. module, arch.exe displays an error message and halts. The default name for the
  12433. SEA to be created is out.exe. arch.exe tries to open the output file with this
  12434. name, this time for writing, and issues a message if the open fails. Arch.exe
  12435. opens out.exe as a binary file to ensure that other functions will not perform
  12436. unwanted conversions on the file. For example, when fopen opens a file in text
  12437. mode, subsequent reads strip out extra carriage returns. Since archive files
  12438. need to contain any kind of file, fopen must treat all files as binary files,
  12439. thus guaranteeing the integrity of each byte in the file.
  12440. If all goes well, arch.exe copies the extraction module to out.exe, and then
  12441. closes it.
  12442. Next, arch.exe attempts to open a file from the command line list. If arch.exe
  12443. can't open the file, it displays a message indicating the name of the file,
  12444. increments count, and forces a jump to the top of the loop. (In this case, the
  12445. failure of a file to open may not indicate a problem. Wild-card expansion in
  12446. the command line sometimes generates the name of a subdirectory, which, of
  12447. course, can't be opened.)
  12448. Next, arch.exe calls fseek and ftell to determine the size of the opened file.
  12449. The call
  12450. fseek( input, 0, SEEK_END );
  12451. moves the file position indicator (an index into the file maintained by the
  12452. FILE type) to one byte after the last byte in the file. A call to ftell
  12453. returns the current file position. The combination
  12454. fseek( input, 0, SEEK_END );
  12455. header.filesize = ftell( input );
  12456. stores the file's size, in bytes, in header.filesize. Another call to fseek,
  12457. this time with argument SEEK_SET,
  12458. fseek( input, 0, SEEK_SET );
  12459. repositions input''s file position indicator to the beginning of the file.
  12460. Depending on how it is built, arch.exe may provide wildcard expansion of
  12461. command line arguments. Most compilers provide an object module which can be
  12462. linked into the program to provide this feature (see the sidebar "Wildcard
  12463. Expansion"). Therefore, after arch.exe reads the command line, the
  12464. command-line arguments may be in the form of file names with or without
  12465. extensions, with partial paths or full paths. The prefix header structure
  12466. expects at most a file name plus extension. arch.exe has to process the
  12467. command line arguments into that form. Some C compilers provide a function
  12468. that does just that job. Unfortunately, it's not a standard function. For
  12469. example, Microsoft C provides _splitpath, which breaks a path into its
  12470. component parts and stores them in strings. Zortech C++ 3.0 supplies the
  12471. function filespecname, which returns a pointer to a string containing the file
  12472. name plus extension. Instead of using a compiler-specific function, I created
  12473. the function filename in Listing 2. filename is a stripped down version of
  12474. _splitpath that extracts the file name plus extension from a path. Arch.exe
  12475. passes filename the path and a character buffer. filename scans the path in
  12476. reverse order, by decrementing a pointer, and stops when it has a full file
  12477. name. The call to filename completes the prefix header data structure.
  12478. Arch.axe calls fwrite to write the prefix header to out.exe. After writing,
  12479. fwrite leaves the file position indicator just beyond the prefix header. After
  12480. copying the current file to out.exe, Arch.exe then closes input in preparation
  12481. for the next file and increments count.
  12482. When there are no more files to be archived, arch.exe writes one final header,
  12483. with header.filesize set to -1L, to the output file. This suffix header serves
  12484. as the end-of-archive mark for the extraction module and completes the SEA.
  12485.  
  12486.  
  12487. Extraction Module -- Detailed Operation
  12488.  
  12489.  
  12490. Listing 3 contains the source code for the extraction module, extr.exe.
  12491. extr.exe reads in prefix headers, and uses the information thus gleaned to
  12492. recreate files.
  12493.  
  12494.  
  12495.  
  12496. The Magic Number
  12497.  
  12498.  
  12499. The extractor must know where the first prefix header starts, which means the
  12500. extractor must know its own length. To get the size information into the
  12501. extraction module, I needed to know the size of extr.exe before I compiled it.
  12502. So I declared a long int, MagicNumber, and initialized it with a dummy value.
  12503. Then I compiled and linked extr.c the usual way. I ran MS-DOS's DIR command to
  12504. obtain extr.exe's file size and used this value to initialize MagicNumber. I
  12505. had to recompile, of course, since I had edited the source code, but the size
  12506. of extr.exe doesn't change. Now MagicNumber tells the extraction module how
  12507. big it is. (I use a batch file to automate keeping the value of MagicNumber
  12508. synchronized with the size of extr.exe. See "Miscellaneous Implementation
  12509. Notes" for some details.)
  12510.  
  12511.  
  12512. Command Line Processing
  12513.  
  12514.  
  12515. When the extraction module begins execution just inside functin main) it first
  12516. checks for arguments on the command line. If the user types in an unknown
  12517. option at the command line, the SEA displays a usage message and exits. When
  12518. argc equals 1 the default action, extraction, is performed. The only option
  12519. extr.exe recognizes is -l(ist), which causes a list of archived files and
  12520. their sizes to be sent to the standard output.
  12521. argv[0] contains the string used to invoke the extraction program, so the
  12522. function call fopen(argv[0], "rb") opens the file that is currently executing.
  12523. The program can open its own .EXE file from disk because the executing image
  12524. is just a copy of the disk file. Using this technique to open the SEA allows
  12525. you to rename out.exe to whatever you want.
  12526.  
  12527.  
  12528. Navigating the File
  12529.  
  12530.  
  12531. The program calls fseek with arguments SEEK_SET and MagicNumber to move the
  12532. file position indicator just past the extraction module, to the beginning of
  12533. the first prefix header. (Remember to adjust MagicNumber if you edit extr.c!)
  12534. In the while loop, fread reads in a prefix header. If it's the suffix header,
  12535. there are no more files to extract, so the program exits the loop and closes
  12536. input. Otherwise, the program attempts to create a file in the current
  12537. directory using the string from the prefix header, header.filename. If a file
  12538. with the same name already exists, the program overwrites it. The messages
  12539. displayed along the way indicate progress. When the program has copied
  12540. header.filesize bytes to the new file, it closes the new file, increments
  12541. count, and starts the next iteration.
  12542. The procedure for listing the component files is the same, except instead of
  12543. copying a file, the program skips the file by calling
  12544. fseek( input, header.filesize, SEEK_CUR );
  12545. which moves the file position indicator header.filesize bytes forward from its
  12546. current position, to the beginning of the next prefix header.
  12547.  
  12548.  
  12549. Miscellaneous Implementation Notes
  12550.  
  12551.  
  12552. The prefix header is a structure declared in Listing 1, sea.h. The first
  12553. member, filename, holds the file's name in an array of characters, as a
  12554. C-style string. The array only needs to be thirteen bytes long in this
  12555. implementation. If you decide to store more than a base name, a dot, and an
  12556. extension, you adjust the array's size accordingly. A long int, filesize,
  12557. contains the file's length.
  12558. If you change the size of extr.exe you must recompile the extraction module. I
  12559. run a little batch file, REMAKE.BAT (Listing 5), from the makefile each time
  12560. extr.exe gets rebuilt, which prints a message to the screen indicating if
  12561. MagicNumber equals the size of extr.exe. The batch file creates a temporary
  12562. file composed of extr.c and a one-line directory listing. An awk program
  12563. (Listing 6), called from the batch file, digs out the file size from the
  12564. directory line and the value used to intialize MagicNumber, compares them, and
  12565. prints a one line report to the screen. (To keep the awk program simple, I put
  12566. a space between MagicNumber's initializer and the semicolon.) I use MKS Awk,
  12567. but other versions should work, too.
  12568. To use the archiver, copy arch.exe and extr.exe to a separate subdirectory on
  12569. your system. arch.exe expects to find extr.exe in the same subdirectory. The
  12570. files to be archived can exist in any subdirectory and on other drives.
  12571. However, the SEA as currently implemented does not store subdirectory or drive
  12572. information. Therefore, when you run the SEA, it will extract all files to the
  12573. same subdirectory. This can be a problem if the archive contains duplicate
  12574. file names from different subdirectories. The extractor will overwrite files
  12575. with duplicate names. If you compiled with Microsoft C and linked with
  12576. setargv.obj as described in the sidebar "Wildcard Expansion," you can use the
  12577. usual MS-DOS wildcards, ? and *. Other compilers may or may not offer wildcard
  12578. expansion as an option. The archiver produces a SEA named out.exe in the
  12579. current directory. You can rename it to anything you want.
  12580. To add compression to the archiver see "A Simple Data-Compression Technique"
  12581. by Ed Ross in the October 1992 issue of The C Users Journal. He describes a
  12582. method of run length encoding. The source code is available on the CUJ code
  12583. disk, or you can download it from one of the online sources listed at the end
  12584. of the table of contents.
  12585. For an extensive introduction to methods of data compression in C, see The
  12586. Data Compression Book by Mark Nelson, from M & T Books. Nelson presents
  12587. explanations and detailed working versions of popular varieties of data
  12588. compression. The final chapter contains a complete compression/archiving
  12589. package, CARMAN.
  12590.  
  12591.  
  12592. Conclusion
  12593.  
  12594.  
  12595. The SEA and archiver I have described are very simple, but useful. Because of
  12596. the SEA's simplicity, programmers should find it easy to modify for their own
  12597. use. The SEA's straightforward structure also makes it useful as a learning
  12598. tool.
  12599. Wildcard Expansion
  12600. MS-DOS does not provide wildcard expansion of command-line arguments to
  12601. programs. Therefore, some compilers provide for wildcard expansion by linking
  12602. extra code into the functions that process command line arguments. For
  12603. example, Microsoft C provides such code in a file named setargv.obj.
  12604. To provide expansion of wildcards from the command line, you must link
  12605. setargv.obj with arch.obj. main calls a routine called _setargv to process
  12606. command line arguments. setargv's default action is to not expand wildcards.
  12607. By linking with setargv.obj, you replace the usual _setargv with one that
  12608. expands wildcards. _setargv. obj is located in the MSC LIB subdirectory. To
  12609. link in _setargv.obj from the command line use something like
  12610. cl arch.c setargv /link /NOE
  12611. You must use the /NOE link option, otherwise you get a "symbol multiply
  12612. defined" error. /NOE instructs the linker not to search extended dictionaries,
  12613. which are lists of symbol locations that speed up searching for references. A
  12614. makefile for Microsoft's NMAKE.EXE utility is provided in Listing 4.
  12615. Borland C++ includes a similar file called wildargs.obj. Zortech's version is
  12616. called _MAINx.OBJ, where the x refers to the model.
  12617. Piggybacking an .EXE File
  12618. My SEA files use the .EXE extension to tell MS-DOS that they are executable
  12619. files. However, they are not normal .EXE files; they've been extended. MS-DOS
  12620. still executes them without any difficulty. An .EXE file has three parts: a
  12621. program file header, relocation tables and a relocatable image of the program.
  12622. The relocation table contains the information MS-DOS uses to adjust references
  12623. to memory locations in the executable image. The program file header contains,
  12624. among other things, information about how much RAM the executable image needs
  12625. to run. Note that the file size returned by DIR and the amount of memory
  12626. needed to run the program are not related. The .EXE file does not even know
  12627. how much room it takes up on disk. This is the trick I used to build an
  12628. archive file. Adding bytes to the end of a .EXE file does not change the
  12629. information in the program file header, so when you run a SEA, only the
  12630. extraction module (i.e., the original extr.exe) gets loaded.
  12631. You can investigate this further using a utility called exehdr.exe that
  12632. Microsoft includes with C. It allows you to examine and change a program file
  12633. header. (Versions of C before 6.0 included a slightly different version called
  12634. exemod.exe.) If you run exehdr.exe on extr.exe, the extraction module, and
  12635. then on out.exe, a SEA that contains the extraction module, all of the numbers
  12636. displayed are the same, except for the file size, which is obtained from the
  12637. disk, rather than the program file header.
  12638. Figure 1 SEA structure
  12639.  
  12640. Listing 1 Prefix header structure
  12641. /* SEA.H */
  12642. /* Copyright 1993 by P.J. LaBrocca
  12643. All rights reserved.
  12644. */
  12645.  
  12646. #ifndef SEA_H
  12647. #define SEA_H
  12648.  
  12649. typedef struct header {
  12650.  
  12651. char filename[20];
  12652. long filesize;
  12653. } HEADER;
  12654.  
  12655. #endif
  12656.  
  12657. /* End of File */
  12658.  
  12659.  
  12660. Listing 2 Archiver
  12661. /* ARCH.C */
  12662. /* Copyright 1993 by P.J. LaBrocca
  12663. All rights reserved.
  12664. */
  12665.  
  12666. #include <stdio.h>
  12667. #include <stdlib.h>
  12668. #include <string.h>
  12669.  
  12670. #include "sea.h"
  12671.  
  12672. /* Forms a filename plus extension,
  12673. if any, from the full or partial path
  12674. pointed to by path and stores it in
  12675. buffer pointed to by name.
  12676. */
  12677. char *filename( char *path, char *name )
  12678. {
  12679. char *p;
  12680.  
  12681. /* Start at last character */
  12682. p = path + strlen( path ) - 1;
  12683. while( p != path ) {
  12684. if( *p == '\\' *P == ':' )
  12685. break;
  12686. --p;
  12687. }
  12688. if( p == path && *p != '\\')
  12689. strcpy( name, path );
  12690. else
  12691. strcpy( name, ++p );
  12692. return name;
  12693. }
  12694.  
  12695. void main( int argc, char **argv )
  12696. {
  12697. FILE *input, *output;
  12698. int c;
  12699. int count = 1;
  12700. HEADER header;
  12701. long i;
  12702. /* char extension[5]; for _splitpath() */
  12703.  
  12704. if( argc == 1 ) {
  12705. printf("Usage: arch file [file ...]\n");
  12706. printf(" Wild cards * and ?.\n");
  12707. exit( 0 );
  12708. }
  12709.  
  12710.  
  12711. /* open extractor module */
  12712. if((input = fopen("extr.exe", "rb")) == NULL) {
  12713. printf( "error opening extr.exe\n" );
  12714. exit( 0 );
  12715. } /* if( ( archive = */
  12716.  
  12717. /* open the final archive file */
  12718. if((output = fopen("out.exe", "wb")) == NULL) {
  12719. printf( "error opening output\n" );
  12720. exit( 0 );
  12721. } /* if( ( output = */
  12722.  
  12723. /* copy extractor to final output file */
  12724. while( ( c = getc( input ) ) != EOF ) {
  12725. putc( c, output );
  12726. } /* while( ( c = */
  12727.  
  12728. fclose( input );
  12729.  
  12730. while( count < argc ) {
  12731. if((input = fopen( argv[count], "rb"))==NULL) {
  12732. printf( "Can't open %s\n", argv[count] );
  12733. ++count;
  12734. continue;
  12735. } /* if( ( input = */
  12736. printf( "Adding %-15s", argv[count] );
  12737. fseek( input, 0, SEEK_END );
  12738. header.filesize = ftell( input );
  12739. fseek( input, 0, SEEK_SET );
  12740.  
  12741. #if 0
  12742.  
  12743. _splitpath( argv[count], NULL, NULL,
  12744. header.filename, extension );
  12745. strcat( header.filename, extension );
  12746. #endif
  12747. filename( argv[count], header.filename );
  12748.  
  12749. fwrite( &header, sizeof( HEADER ), 1, output );
  12750.  
  12751. for( i = 0; i < header.filesize; ++i ) {
  12752. putc( getc( input ), output );
  12753. } /* for( i = 0; i <*/
  12754. fclose( input );
  12755. printf("Done!\n");
  12756. ++count;
  12757. } /* while( ) */
  12758. header.filesize = -1L;
  12759. fwrite( &header, sizeof( HEADER ), 1, output );
  12760. fclose( output );
  12761. } /* main */
  12762.  
  12763. /* End of File */
  12764.  
  12765.  
  12766. Listing 3 Extraction module
  12767. /* EXTR.C */
  12768. /* Copyright 1993 by P.J. LaBrocca All rights reserved. */
  12769. /* The Extraction Module */
  12770.  
  12771.  
  12772. #include <stdio.h>
  12773. #include <stdlib.h>
  12774. #include <string.h>
  12775.  
  12776. #include "sea.h"
  12777.  
  12778. void bailout( void )
  12779. {
  12780. printf( "\nUsage: archiveName [ -1 ]\n" );
  12781. printf( " archiveName Extract files.\n" );
  12782. printf( " archiveName -1 List files.\n" );
  12783. exit( 0 );
  12784. }
  12785.  
  12786. void main( int argc, char **argv )
  12787. {
  12788. FILE *input, *output;
  12789. /* size of extraction module */
  12790. long MagicNumber = 10867 ; /* sic */
  12791. int count = 1;
  12792. HEADER header;
  12793. long i;
  12794. int sw;
  12795.  
  12796. /* verify user input */
  12797. if( argc > 2 ) {
  12798. bailout();
  12799. }
  12800. if( argc == 1 )
  12801. ;
  12802. else if( strcmp( argv[1], "-1" ) == 0 )
  12803. sw = '1';
  12804. else
  12805. bailout();
  12806.  
  12807. /* open self-extracting archive */
  12808. if( ( input = fopen( argv[0], "rb" ) ) == NULL ) {
  12809. printf( "error opening %s\n", argv[0] );
  12810. exit( 0 );
  12811. } /* if( ( input = */
  12812.  
  12813. /* skip extraction module */
  12814. fseek( input, MagicNumber, SEEK_SET );
  12815.  
  12816. switch( sw ) {
  12817. default: /* extract contents of archive */
  12818. while( 1 ) {
  12819. fread(&header, sizeof(HEADER), 1, input);
  12820. if(header.filesize == -1L)
  12821. break;
  12822. if((output=fopen(header.filename, "wb"))==NULL){
  12823. printf("error opening %s\n",header.filename);
  12824. exit(0);
  12825. } /* if(( output = */
  12826.  
  12827. printf("Creating %-55s", header.filename);
  12828. for( i = 0; i < header.filesize; ++i ) {
  12829. putc( getc( input ), output );
  12830.  
  12831. } /* for( i = 0; i < */
  12832. printf("Done!\n");
  12833. fclose( output );
  12834. ++count;
  12835. } /* while( ) */
  12836. break;
  12837. case 'l': /* list contents of archive */
  12838. while( 1 ) {
  12839. fread(&header, sizeof(HEADER), 1, input):
  12840. if( header.filesize == -1L )
  12841. break;
  12842. printf(" %-15s%91d\n", header.filename,
  12843. header.filesize);
  12844. /* Skip file contents. */
  12845. fseek( input, header.filesize, SEEK_CUR );
  12846. ++count;
  12847. } /* while( ) */
  12848. break;
  12849. } /* switch */
  12850.  
  12851. fclose( input );
  12852. } /* main */
  12853. /* End of File */
  12854.  
  12855.  
  12856. Listing 4 Makefile for arch.exe and extr.exe
  12857. # makefile for ARCH.EXE and EXTR.EXE
  12858. # Copyright 1993 by P.J. LaBrocca
  12859. # All rights reserved.
  12860. #
  12861.  
  12862. #CC =cl /c /AL /Od /Zi
  12863. CC =cl /c /AL
  12864.  
  12865. #CV = /CO
  12866. CV =
  12867.  
  12868. .c.obj:
  12869. $(CC) $*.c
  12870.  
  12871. all : arch.exe extr.exe
  12872.  
  12873. arch.exe :arch.obj
  12874. link arch+setargv,,, /NOE /NOI $(CV) ;
  12875.  
  12876. arch.obj : arch.c sea.h
  12877.  
  12878. # remake.bat requires awk
  12879. extr.exe : extr.obj
  12880. link extr, , , /NOI $(CV) ;
  12881. # remake.bat
  12882.  
  12883. extr.obj : extr.c sea.h
  12884.  
  12885. backup: backup1 backup2
  12886.  
  12887. backup1: arch.c makefile extr.c archart.doc sea.h
  12888. cp -m $? a:\archart
  12889. touch backup1
  12890.  
  12891.  
  12892. backup2: remake.bat remake.awk
  12893. cp -m $? a:\archart
  12894. touch backup2
  12895.  
  12896. hcopy: hcopy1
  12897.  
  12898. hcopy1: arch.c makefile extr.c sea.h archart.txt
  12899. pr -W -e4 -n' '3 $? > prn
  12900. touch hcopy1
  12901.  
  12902. # End of File
  12903.  
  12904.  
  12905. Listing 5 A hatch file to check MagicNumber
  12906. rem REMAKE.BAT
  12907. dir extr.exe > %tmp%\extr.tmp
  12908. cat extr.c >> %tmp%\extr.tmp
  12909. awk -f remak.awk %tmp%\extr.tmp
  12910. del %tmp%\extr.tmp
  12911.  
  12912.  
  12913. Listing 6 An awk file to check the size of MagicNumber
  12914. # REMAKE.AWK
  12915.  
  12916. $1 == "EXTR" && $2 == "EXE" {ml = $3}
  12917. $1 == "long" && $2 == "MagicNumber" {m2 = $4}
  12918.  
  12919. END {
  12920. if( ml != m2 } {
  12921. printf("\n\tInitialize MagicNumber to %d", ml)
  12922. print" ; and recompile EXTR.C!\n"
  12923. }
  12924. if( ml == m2 )
  12925. print "\n\tMagicNumber is ok.\n"
  12926. }
  12927.  
  12928. # End of File
  12929.  
  12930.  
  12931.  
  12932.  
  12933.  
  12934.  
  12935.  
  12936.  
  12937.  
  12938.  
  12939.  
  12940.  
  12941.  
  12942.  
  12943.  
  12944.  
  12945.  
  12946.  
  12947.  
  12948.  
  12949.  
  12950.  
  12951.  
  12952.  
  12953.  
  12954. C++ Memory Management
  12955.  
  12956.  
  12957. P.J. Plauger
  12958.  
  12959.  
  12960. P.J. Plauger is senior editor of The C Users Journal. He is convenor of the
  12961. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  12962. latest books are The Standard C Library, and Programming on Purpose (three
  12963. volumes), all published by Prentice-Hall. You can reach him at
  12964. pjp@plauger.com.
  12965.  
  12966.  
  12967. The authors begin by admitting that memory management on the 80X86 family of
  12968. microprocessors is byzantine. I'd call that an understatement. I once
  12969. estimated that hundreds of programmer years were expended simply because
  12970. someone at IBM decided in the early 1960s that System 360 could do quite
  12971. nicely with 12-bit displacements in essentially all of its machine
  12972. instructions. At the time, I never thought that a future decision would ever
  12973. be as costly. IBM, Intel, and others (acting separately and in concert) have
  12974. proved me wrong with the IBM PC memory layout and access machinery.
  12975. I won't bore you with a recitation of all those definitions. You know:
  12976. conventional memory, upper memory, high memory, expanded memory, extended
  12977. memory, and so forth. Chances are you care intimately about this stuff and
  12978. know far more than I. Or you neither know nor care how all that PC software
  12979. manages to find enough storage to get the job done. But there is also a real
  12980. chance that you fall somewhere in the middle -- you'd love to write large
  12981. programs that run under MS-DOS, but blanch at the thought of mastering all
  12982. that memory-management nonsense.
  12983. If you fall in that third category, or even the first, then this is a book you
  12984. should take a serious look at. It is written in the tradition of the best
  12985. articles in The C Users Journal. You will find oodles of code that does all
  12986. the various bits of magic needed to do battle with EMS, XMS, and even swapping
  12987. memory to disk. The presentation starts with the (reasonably) simple and
  12988. progresses in stages to a comprehensive solution that hides most of the uglies
  12989. behind a clean interface. Along the way, the writing is generally clear and to
  12990. the point.
  12991. You might even buy the book just for the code disk in the back. The authors
  12992. assert that it works with both Borland C++ v3.1 and Microsoft C++ v7.0. I
  12993. didn't try it out, mostly because I cling desperately to my ignorance of PC
  12994. memory management. But I have to say that the code listings and the
  12995. presentation certainly look credible. After reading thousands of manuscripts,
  12996. I like to think that I've developed an eye for this sort of thing.
  12997. Dorfman and Neuberger give you working C++ code to interface to EMS and XMS.
  12998. That's laudable in its own right. But it also amounts to rather thin icing
  12999. over a lumpy underlying cake. Most important, they end with a virtual memory
  13000. manager (VMM) that manages whatever kind of memory it can get its hands on.
  13001. And that can include what they call "fast" memory -- the conventional or upper
  13002. memory that reads and writes as fast as the rest of the stuff in your program.
  13003. Their VMM interface is simpler than any of its building blocks. Moreover, the
  13004. underlying implementation is willing to adapt over a broad range of
  13005. possibilities.
  13006. I can only carp about a couple of small items, after a fairly cursory review
  13007. of this book. One is that the authors tend to be a bit cute in their writing.
  13008. They should think twice about quoting words and phrases. Almost every use
  13009. should be replaced by a font shift, a less clichéd phrase, or straight text
  13010. spoken without seeming apology. But that is a minor blemish on otherwise clear
  13011. writting.
  13012. The other carp is the choice of C++ as a presentation language. I found very
  13013. few places where they arguably took advantage of the greater power of C++ over
  13014. C. They might have a more usable product written in C. On the other hand, they
  13015. could have made better use of the information hiding made possible by C++. But
  13016. that too is a minor issue, given the near universal packaging of C and C++
  13017. compilers these days, particularly under MS-DOS.
  13018. In summary, this book looks to be a valuable asset if you want to learn more
  13019. about the various ways to stretch memory under MS-DOS. It is equally useful is
  13020. you don't want to know any more than absolutely necessary, but still need to
  13021. get the job done. That makes it a double winner, in my book.
  13022. Title: C++ Memory Management
  13023. Author: Len Dorfman and Marc J. Neuberger
  13024. Publisher: Windcrest/McGraw-Hill, New York, 1993
  13025. Price: $32.95
  13026. ISBN: 0-8306-4288-9
  13027. Pages: 293
  13028.  
  13029.  
  13030.  
  13031.  
  13032.  
  13033.  
  13034.  
  13035.  
  13036.  
  13037.  
  13038.  
  13039.  
  13040.  
  13041.  
  13042.  
  13043.  
  13044.  
  13045.  
  13046.  
  13047.  
  13048.  
  13049.  
  13050.  
  13051.  
  13052.  
  13053.  
  13054.  
  13055.  
  13056.  
  13057.  
  13058.  
  13059.  
  13060.  
  13061.  
  13062.  
  13063.  
  13064.  
  13065.  
  13066. Standard C
  13067.  
  13068.  
  13069. The Header <exception>
  13070.  
  13071.  
  13072.  
  13073.  
  13074. P. J. Plauger
  13075.  
  13076.  
  13077. P.J. Plauger is senior editor of The C Users Journal. He is convenor of the
  13078. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  13079. latest books are The Standard C Library, and Programming on Purpose (three
  13080. volumes), all published by Prentice-Hall. You can reach him at
  13081. pjp@plauger.com.
  13082.  
  13083.  
  13084.  
  13085.  
  13086. Introduction
  13087.  
  13088.  
  13089. This is the fourth installment in a series on the draft standard being
  13090. developed for the C++ library. (See "Standard C: Developing the Standard C++
  13091. Library," CUJ, October 1993, "Standard C: C++ Library Ground Rules," CUJ,
  13092. November 1993, and "Standard C: The C Library in C++," CUJ, December 1993.) I
  13093. am slowly working my way through the entire library, much as I did with the
  13094. Standard C library in years past. The major difference is that this journey
  13095. travels far more uncharted territory. So I endeavor to provide consistent
  13096. landmarks along the way. For example, here, once again, is the overall
  13097. structure of the draft C++ library standard:
  13098. (0) introduction, the ground rules for implementing and using the Standard C++
  13099. library
  13100. (1) the Standard C library, as amended to meet the special requirements of a
  13101. C++ environment
  13102. (2) language support, those functions called implicitly by expressions or
  13103. statements you write in a C++ program
  13104. (3) iostreams, the extensive collection of classes and functions that provide
  13105. strongly typed I/O
  13106. (4) support classes, classes like string and complex that pop up in some form
  13107. in every library shipped with a C++ compiler
  13108. Thus far I have described (0) introduction and (1) the Standard C library. I
  13109. have to confess, however, that I'm aiming at a moving target. Both those items
  13110. have changed in an important regard, thanks to the introduction of namespaces
  13111. into C++. Namespaces went into the language at the March 1993 meeting in
  13112. Munich. The library adopted a style for using namespaces at the July 1993
  13113. meeting in San Jose. But I won't confuse you by rehashing those topics just to
  13114. make them current. That would be an endless battle, these days. You can expect
  13115. changes in C++ every four months for the next year or two. We can only hope
  13116. that the changes will grow ever smaller as the draft C++ standard converges to
  13117. final form.
  13118. More important, namespaces are so new that no commercial compiler I know of
  13119. supports them yet. Nor are many likely to do so in the near future. And even
  13120. when they're more widely available, you can bet that backward compatibility
  13121. will remain an issue. I expect that namespaces will remain under the hood for
  13122. some time to come. In the medium to long term, they are most likely to be of
  13123. interest only to the more sophisticated C++ programmers.
  13124. So I will begin, as I intended, with a new topic this month, (2) language
  13125. support. It contains no small amount of news in the area of exceptions, which
  13126. have been implemented by several C++ compilers. And it deals with issues of
  13127. much wider interest, such as the global operator new and operator delete, plus
  13128. a few newer odds and ends. For this installment, I focus only on the library
  13129. support for exception handling.
  13130. This section of the draft C++ library standard is called language support
  13131. because it, more than any other part of the C++ library, is on rather intimate
  13132. terms with the language proper. A compiler will generate code that uses the
  13133. classes declared in this section, or that implicitly calls functions defined
  13134. here. In the case of exception classes, you will also find quite a few fellow
  13135. travelers, not directly connected to language support. But the tie is there,
  13136. nevertheless.
  13137.  
  13138.  
  13139. Exceptions
  13140.  
  13141.  
  13142. Exceptions represent a significant departure in C++ from past programming
  13143. practice in C. Much of what you write in C++ translates one for one to very
  13144. similar C code. The rest may get longer winded, and a bit harder to read, but
  13145. it's still conventional C. Exception handling, however, changes the underlying
  13146. model of how functions get called, automatic storage gets allocated and freed,
  13147. and control gets passed back to the calling functions.
  13148. A compiler can generate reasonably portable C code to handle exceptions, but
  13149. that code can have serious performance problems -- even for programs that
  13150. don't use exceptions. The very possibility that an exception can occur in a
  13151. called function changes how you generate code for the caller. Alternatively, a
  13152. compiler can generate code directly that can't quite be expressed in C -- and
  13153. face a different set of problems. It may be hard to mix such C++ code with
  13154. that generated from C or another programming language. Perhaps you can see now
  13155. why C++ vendors have generally been slow to add this important new feature to
  13156. the language.
  13157. What makes exception handling important is that it stylizes a common operation
  13158. expressible in C only in a rather dirty fashion. You can think of exception
  13159. handling, in fact, as a disciplined use of the notorious functions setjmp and
  13160. longjmp, declared in <setjmp.h>. (Strictly speaking, setjmp is a macro, but
  13161. let's not pursue that distraction for now.)
  13162. In a C program, you call setjmp at a point to which you expect to "roll back."
  13163. The function memorizes enough context to later reestablish the roll-back
  13164. point, then returns the value zero. A later call to longjmp can occur anywhere
  13165. within the same function or a function called from that function, however deep
  13166. in the call stack. By unspecified magic, the call stack gets rolled back and
  13167. control returns once again from setjmp. The only difference is, this time you
  13168. can tell from the nonzero return value that a longjmp call initiated the
  13169. return.
  13170. That all adds up to a clever bit of machinery, used to pull off all sorts of
  13171. error recovery logic over the past couple of decades. The only trouble is,
  13172. it's too clever by half. Many implementations have trouble determining how to
  13173. roll back all the automatic storage properly. The C Standard is obligingly
  13174. vague on the subject, making life easier on the implementors at the expense of
  13175. being harder on those wishing to write portable and robust code. Nobody
  13176. pretends that <setjmp.h> is an elegant piece of design.
  13177. In C++, matters are much worse. That language prides itself on cradle-to-grave
  13178. control of objects, particularly nontrivial ones. You are assured that every
  13179. object gets constructed exactly once, before anybody can peek at its stored
  13180. values. And you are promised with equal fervor that every object gets
  13181. destroyed exactly once. Thus, you can allocate and free auxiliary storage for
  13182. an object with a discipline that ensures no files are left open, or no memory
  13183. gets lost, in the hurly burly of execution.
  13184. longjmp sabotages the best efforts of C++ compilers to maintain this
  13185. discipline. In rolling back the call stack, the older C function cheerfully
  13186. bypasses all those implicit calls to destructors strewn about by the C++
  13187. compiler. Promises get broken, files remain open, storage on the heap gets
  13188. lost. The draft C++ standard leaves <setjmp.h> in the library for upward
  13189. compatibility. But it discourages the use of these heavy handed functions in
  13190. the neighborhood of "real" C++ code with nontrivial destructors.
  13191. Enter exceptions. In modern C++, you don't report a nasty error by calling
  13192. longjmp to roll back to a point established by setjmp. Instead, you evaluate a
  13193. throw expression to roll back to a catch clause. The throw expression names an
  13194. object whose type matches that expected by the catch clause. You can even
  13195. examine the object to get a hint about what caused the exception. It's kind of
  13196. like calling a function with a single argument, only you're not always sure
  13197. where the function actually resides. And the function is further up the call
  13198. stack instead of one level further down.
  13199. Most important of all, none of those destructors get skipped in the process of
  13200. rolling back the call stack. If that sounds like a nightmare in bookkeeping to
  13201. you, you're absolutely right. Somehow, the executing code must at all times
  13202. have a clear notion of what destructors are pending before control can pass
  13203. out of a given block or a given function. It must also deal with exceptions
  13204. thrown in constructors and destructors, and exceptions thrown while processing
  13205. earlier exceptions. Kids, don't try this at home.
  13206.  
  13207.  
  13208. The header <exception>
  13209.  
  13210.  
  13211. So this fancier machinery is now in the draft C++ standard. All that remains
  13212. is to decide what to do with it. You can get a few hints from other
  13213. programming languages. Ada, to name just one, has had exceptions for over a
  13214. decade. Their very presence changes how you design certain interfaces and how
  13215. you structure programs that must respond to nasty errors. The one thing we
  13216. know for sure is that you must develop a style for using exceptions that fits
  13217. the language as a whole, then use it consistently.
  13218. That has serious implications for the Standard C++ library. Traditionally, of
  13219. course, the library has thrown or caught no exceptions. (There weren't any
  13220. such critters to throw!) But it's a poor advertisement for this new feature if
  13221. the library itself makes no use of exceptions. Put more strongly, the Standard
  13222. C++ library has a moral obligation to set a good example. Many programmers
  13223. will use only the exceptions defined in the library. Others will model their
  13224. own on what they see used by the library. Thus, the library is duty bound to
  13225. set a good example for the children.
  13226. Most decisions about the Standard C++ library are made within the Library
  13227. Working Group (or LWG) of X3J16/WG21, the joint ANSI/ISO committee developing
  13228. the draft C++ standard. Early on, the LWG committed to using exceptions as
  13229. part of the error reporting machinery of the library. Not everyone is happy
  13230. with this decision. Some people object to this decision because they don't
  13231. want to incur the inevitable overheads of exception handling in every program
  13232. that touches the library -- and that's essentially every program you write in
  13233. C++. Others object because of the putative difficulties of validating a
  13234. program that throws exceptions. Some projects require that the software
  13235. vendors assert that exceptions can never be thrown. So the decision to use
  13236. exceptions in the library was not lightly made.
  13237. Only recently has the LWG agreed on an overall structure. What I present here
  13238. was approved by the joint committee as recently as November 1993. But aside
  13239. from a few name changes and other small tweaks, it is likely to survive
  13240. reasonably unchanged.
  13241. All the relevant declarations and class definitions for exception handling can
  13242. be had by including the header <exception>. (Note the absence of a trailing
  13243. .h, the hallmark of new C++ headers.) Within this header you can find the
  13244. definition for class xmsg, the mother of all exceptions thrown by the library.
  13245. (Yes, the name is horrid -- it's very likely to be changed to exception in the
  13246. near future.) Listing 1 shows at least one way that this class can be spelled
  13247. out.
  13248. The basic idea is that each exception has three null-terminated message
  13249. strings associated with it:
  13250. what -- telling what caused the exception
  13251. where -- telling where it occurred
  13252. why -- telling why it occurred
  13253.  
  13254. Some exceptions may use only the first one or two messages, leaving the later
  13255. pointers null.
  13256. The next important notion is that an exception should have private copies (on
  13257. the heap, presumably) of all these message strings. A typical exception
  13258. constructor allocates storage on the heap, copies the strings, and sets the
  13259. flag alloced. That way, the destructor knows to free the storage once the
  13260. exception has been processed.
  13261. But then why the flag if this is the preferred mode of operation? Well, one
  13262. important exception derived from this base class is xalloc. It is thrown by
  13263. operator new when it fails to allocate storage. (More on this in a later
  13264. installment.) The last thing you want to do is try to copy strings onto the
  13265. heap when you have to report that there's no more room on the heap! Thus, the
  13266. special protected constructor that lets you specify no copying of strings. Of
  13267. course, anyone using this constructor had better provide strings with a
  13268. sufficiently long lifetime, or trouble ensues. That's why this form is
  13269. discouraged, except where absolutely necessary.
  13270.  
  13271.  
  13272. Throwing an Exception
  13273.  
  13274.  
  13275. You'd think then that all you have to do to throw an exception is write
  13276. something like:
  13277. throw xmsg("bad input record");
  13278. You can certainly do so, but that is not the preferred method. Instead, for
  13279. any exception object ex, you're encouraged to call ex.raise(). That function
  13280. does three things:
  13281. First it calls (*ex.handler)(*this) to call the raise handler. The default
  13282. behavior is to do nothing, but you can hijack any thrown exception by
  13283. providing your own handler with a call to xmsg::set_raise_handler.
  13284. Then it calls the virtual member function do_raise. That permits you to hijack
  13285. thrown exceptions only for some class derived from xmsg.
  13286. Finally it evaluates the expression throw *this.
  13287. The first escape hatch is for embedded systems and those projects I indicated
  13288. above that abhor all exceptions. You can reboot, or longjmp to some recovery
  13289. point (and to beck with the skipped destructors).
  13290. The second is best illustrated by the derived class reraise, shown in Listing
  13291. 2. It overrides the definition of do_raise in a special way. The override
  13292. evaluates the expression throw, which "rethrows" an exception currently being
  13293. processed. It turns out that iostreams has an occasional need to pass an
  13294. exception up the line to another handler. I invented reraise as a way to do
  13295. this, but it looks to be generally useful.
  13296. The third thing is to do what exception classes were invented to do in the
  13297. first place. By having all library exceptions be thrown through this
  13298. machinery, however, the class meets the needs of several constituencies.
  13299.  
  13300.  
  13301. Exception Hierarchy
  13302.  
  13303.  
  13304. There's still more to library exceptions. Figure 1 shows a whole hierarchy of
  13305. classes derived from xmsg. Some are defined in other headers, but most are to
  13306. be found in <exception>. The basic partitioning is into two groups:
  13307. logic errors, derived from class xlogic, which report errors that you can, in
  13308. principle, detect and avoid when writing the program
  13309. runtime errors, derived from class xruntime, which report errors that you
  13310. detect only when you run the program
  13311. The former category is for those "can't happen" events that are often too hard
  13312. to really prevent, at least until after some thorough debugging. The latter is
  13313. for surprises that happen during program execution, such as running out of
  13314. heap or encountering bad input from a file.
  13315. Listing 3 shows the class xlogic and Listing 4 shows the class xruntime. Note
  13316. the slight asymmetry. The latter class has an extra low-level constructor, as
  13317. I described earlier, which is supposed to be used only by xalloc. Two more
  13318. classes are derived from these to report mathematical errors. Listing 5 shows
  13319. the class xdomain and Listing 6 shows the class xrange. A domain error occurs
  13320. when you call a mathematical function with arguments for which its behavior is
  13321. not defined (such as taking the real square root of -5). A range error occurs
  13322. when the result of a mathematical function is defined in principle but not
  13323. representable in practice (such as raising e to the 10,000 power).
  13324. Finally, Listing 7 shows the class bad-cast. It is the one exception thrown
  13325. implicitly by statements generated by the compiler. C++ now includes dynamic
  13326. casts which, in certain contexts, yield a null pointer if the cast is not
  13327. permissible at runtime. If the context also requires that a reference be
  13328. initialized, the executable code throws a badcast exception instead. (A
  13329. reference can never be to a nonexistant object.)
  13330.  
  13331.  
  13332. Terminate and Unexpected Handlers
  13333.  
  13334.  
  13335. Exception processing code can also call two additional functions:
  13336. terminate, when exception handling must be abandoned for any of several
  13337. reasons
  13338. unexpected, when a function throws an exception that is not listed in its
  13339. (optional) exception specification.
  13340. A terminate condition occurs:
  13341. when the exception handling mechanism cannot find a handler for a thrown
  13342. exception
  13343. when the exception handling mechanism finds the execution stack corrupted
  13344. when a destructor called during execution stack unwinding caused by an
  13345. exception tries to transfer control to a calling function by throwing an
  13346. exception
  13347. The default behavior of terminate is to call abort, while the default behavior
  13348. of unexpected is to call terminated. As usual in C++, however, you can provide
  13349. your own flavors of these functions. A call to set_terminate lets you specify
  13350. a pointer to a new function that must still somehow terminate the program. A
  13351. call to set_unexpected lets you specify a pointer to a new function that can
  13352. itself throw (or rethrow) an exception or terminate program execution.
  13353.  
  13354.  
  13355. Conclusion
  13356.  
  13357.  
  13358. As you can see, the facilities provided by <exception> give you considerable
  13359. latitude in reporting and handling exceptions. The C++ library uses this
  13360. machinery exclusively, so you can control what the library does with
  13361. exceptions. You can even prevent the library from actually evaluating any
  13362. throw expressions.
  13363. Given our limited experience to date with using expressions in C++, I'm fairly
  13364. confident that this is mostly a good design. Time, of course, will tell us
  13365. better how well we've done.
  13366. Figure 1 Exception class hierarchy
  13367. xmsg
  13368.  xlogic
  13369.  xdomain
  13370.  badcast
  13371.  invalid_argument (<bits>, etc.)
  13372.  length_error (<string>, etc.)
  13373.  out_of_range (<string>, etc.)
  13374.  xruntime
  13375.  xrange
  13376.  xalloc (<new>)
  13377.  
  13378.  failure (<ios>)
  13379.  
  13380. Listing 1 Class xmsg
  13381. class xmsg {
  13382. public:
  13383. typedef void(*raise_handler)(xmsg&);
  13384. private:
  13385. const char *what, *where, *why; // exposition only
  13386. int alloced; // exposition only
  13387. static raise_handler handler; // exposition only
  13388. protected
  13389. virtual void do_raise();
  13390. xmsg(const char *what_arg, const char *where_arg,
  13391. const char *why_arg, int copyfl);
  13392. public:
  13393. xmsg(const char *what_arg = 0, const char *where_arg = 0,
  13394. const char *why_arg = 0);
  13395. virtual ~xmsg();
  13396. void raise();
  13397. const char *what() const;
  13398. const char *where() const;
  13399. const char *why() const;
  13400. static raise_handler set_raise handler(raise_handler handler_arg);
  13401. };
  13402.  
  13403. // End of File
  13404.  
  13405.  
  13406. Listing 2 Class reraise
  13407. class reraise : public xmsg {
  13408. protected:
  13409. virtual void do_raise();
  13410. public:
  13411. reraise();
  13412. virtual ~reraise();
  13413. };
  13414.  
  13415. // End of File
  13416.  
  13417.  
  13418. Listing 3 Class xlogic
  13419. class xlogic : public xmsg {
  13420. protected:
  13421. virtual void do_raise();
  13422. public:
  13423. xlogic(const char *what_arg = 0, const char* where_arg = 0,
  13424. const char *why_arg = 0);
  13425. virtual ~xlogic();
  13426. };
  13427.  
  13428. //End of File
  13429.  
  13430.  
  13431. Listing 4 Class runtime
  13432. class xruntime : public xmsg {
  13433. protected:
  13434. virtual void do_raise();
  13435. xruntime(const char *what_arg, const char *where_arg,
  13436. const char *why_arg, int copyfl);
  13437.  
  13438. public:
  13439. xruntime(const char *what_arg = 0, const char *where_arg = 0,
  13440. const char *why_arg = 0;
  13441. virtual ~xruntime();
  13442. };
  13443.  
  13444. //End of File
  13445.  
  13446.  
  13447. Listing 5 Class xdomain
  13448. class xdomain : public xlogic {
  13449. protected:
  13450. virtual void do_raise();
  13451. public:
  13452. xdomain(const char *what_arg = 0, const char *where_arg = 0,
  13453. const char *why_arg = 0);
  13454. virtual ~xdomain();
  13455. };
  13456.  
  13457. // End of File
  13458.  
  13459.  
  13460. Listing 6 Class xrange
  13461. class xrange : public xruntime {
  13462. protected:
  13463. virtual void do_raise();
  13464. public:
  13465. xrange(const char *what_arg = 0, const char *where_arg = 0,
  13466. const char *why_arg = 0);
  13467. virtual ~xrange();
  13468. };
  13469.  
  13470. // End of File
  13471.  
  13472.  
  13473. Listing 7 class badcast
  13474. class badcast : public xlogic {
  13475. protected:
  13476. virtual void do_raise();
  13477. public:
  13478. badcast();
  13479. virtual ~badcast();
  13480. };
  13481.  
  13482. // End of File
  13483.  
  13484.  
  13485.  
  13486.  
  13487.  
  13488.  
  13489.  
  13490.  
  13491.  
  13492.  
  13493.  
  13494.  
  13495.  
  13496.  
  13497.  
  13498.  
  13499.  
  13500.  
  13501. Questions & Answers
  13502.  
  13503.  
  13504. A Tricky (But Important) Type Distinction
  13505.  
  13506.  
  13507.  
  13508.  
  13509. Kenneth Pugh
  13510.  
  13511.  
  13512. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++
  13513. language courses for corporations. He is the author of C Language for
  13514. programmers and All On C, and was a member of the ANSI C committee. He also
  13515. does custom C programming for communications, graphics, image databases, and
  13516. hypertext. His address is 4201 University Dr., Suite 102, Durham, NC 27707.
  13517. You may fax questions for Ken to (919) 489-5239. Ken also receives email at
  13518. kpugh@allen.com (Internet) and on Compuserve 70125,1142.
  13519.  
  13520.  
  13521.  
  13522.  
  13523. Heap or Stack
  13524.  
  13525.  
  13526. Regarding your Q?/A! column "Heap or Stack -- Which Should You Use" in the
  13527. October 1993 CUJ: On page 122, col. 1, bottom, you state "The type of
  13528. char_matrix[4] is pointer to char...". Actually, it is "array of 10 char." You
  13529. make this distinction clear elsewhere in the article. Like all such array
  13530. pointer differences, this one really only becomes visible if you do
  13531. sizeof(char_matrix[4]), which reports 10, not sizeof(char*). I enjoy your
  13532. articles -- keep up the good work. This is a mere quibble, but I thought you'd
  13533. like to get things exactly right.
  13534. Craig Berry
  13535. Thanks for the sharp-eyed correction. For readers who have lost their October
  13536. issue, the declaration was:
  13537. #define NUMBER_OF_CHARS 10
  13538. #define NUMBER_OF_STRINGS 5
  13539. char char_matrix[NUMBER_OF_STRINGS]
  13540. [NUMBER_OF_CHARS];
  13541. char_matrix[4] is an array of 10 chars, as Mr. Berry notes. Its type reduces
  13542. to char *, so you can code the following without warning:
  13543. char *p_char = char_matrix[4];
  13544.  
  13545.  
  13546. Char Pointers
  13547.  
  13548.  
  13549. I really enjoyed your article in the October issue of CUJ. The examples that
  13550. you presented are very practical examples of allocating memory for arrays.
  13551. Most books on C fail to provide such practical examples and assume that
  13552. everyone simply defines strings inside code hardwire style such as:
  13553. char char_array[3] = "123";
  13554. I wish I had had a copy of your article four years ago when I had to figure
  13555. out how to allocate strings through experimentation. Keep up the good work!
  13556. Russell Thrasher
  13557. Austin, TX
  13558. Thanks for your feedback. If you object to the literal "123" appearing in the
  13559. source code, you can bet that C programming books define strings this way for
  13560. the sake of simplicity. As a general rule, it's best to avoid scattering
  13561. literals, strings, or numeric constant definitions throughout the body of a
  13562. program. Usually, these constants need only appear in #defines at the
  13563. beginning of the source file where they're easy to find. In some cases you can
  13564. eliminate constants from the source file entirely by reading the constants
  13565. into the program from a seperate file at run time.
  13566. Keeping string constant definitions out of a program's source files allows you
  13567. to alter the user interface appearance without changing the executable
  13568. program. This capability is especially important in the international market,
  13569. where you may need to change the (human) language of a user interface. The
  13570. example in the October issue demonstrated one way of eliminating constant
  13571. definitions from the source code entirely, but there are lots of other
  13572. methods.
  13573. For example, the language features of UNIX (NLS), such as the message catalog,
  13574. provide a standard method for storing strings. You can use the resource files
  13575. for the MacIntosh and for Microsoft Windows to store strings in a program. You
  13576. can configure the .Xdefaults file for X-Window (Motif and Open-Look) to store
  13577. all strings for an application. Finally, you can find commercial products that
  13578. will extract strings from a code file and place them into files similar to
  13579. that referenced in the October example.
  13580.  
  13581.  
  13582. A Class for String Storage
  13583.  
  13584.  
  13585. Following along the lines of the previous letter, I will show how C++ classes
  13586. can eliminate most char *pointers and the problems they create, as well as cut
  13587. down on portability problems between various implementations of strings.
  13588. To start, I define a String class as:
  13589. class String
  13590. {
  13591. public:
  13592. String(const char * string);
  13593. operator const char *();
  13594. ...
  13595. private:
  13596. char * data;
  13597. ...
  13598.  
  13599. };
  13600. At this point, I don't specify much of an implementation, as that should not
  13601. be of concern to the user. I include the cast operator to a const char *only
  13602. for back-fitting to functions that require char *pointers. In a more abstract
  13603. interface, all functions in the program would only require either a String or
  13604. a String & (reference to a String) parameters.
  13605. A Collection_of_strings class could have the following interface:
  13606. class Collection_of_strings
  13607. {
  13608. public:
  13609. Collection_of_strings(const char * identifier);
  13610. ~Collection_of_strings();
  13611. String get_string_by_index(unsigned int index);
  13612. ...
  13613. private:
  13614. String * strings;
  13615. unsigned int number_of_strings;
  13616. };
  13617. The user of this class might code a header file as follows:
  13618. #define MY_STRINGS_IDENTIFIER "my_string.dat"
  13619. // These are the names for each string which represents
  13620. // its purpose
  13621. #define ERROR_STRING 0
  13622. #define TITLE_STRING 1
  13623. ...
  13624. And the user's program might look like this:
  13625. void main()
  13626. {
  13627. Collection_of_strings strings(MY_STRINGS_IDENTIFIER);
  13628. String a_string;
  13629. ...
  13630. a_string = strings.get_string_by_index(ERROR_STRING);
  13631. ...
  13632. printf("%s", (const char *) a_string);
  13633. // or whatever else you need to do with it
  13634. ...
  13635. }
  13636. Note that this program does not need to concern itself with how the strings
  13637. are stored in memory or on disk. The only information this program requires is
  13638. the identifier of the set of strings and names or identifiers for all the
  13639. strings.
  13640. Let's look at possible implementations of Collection_of_strings. The
  13641. MY_STRING_IDENTIFIER might either identify an actual data file or a subsection
  13642. of a standard file. The constructor would open the appropriate file. The
  13643. constructor could read all the strings into a memory array and close the file.
  13644. get_string_by_index( unsigned int index ) would use the index to locate the
  13645. appropriate String and return it.
  13646. Alternatively, the constructor would perform an open and the destructor a
  13647. close. get_string_by_index() would use the index to read the corresponding
  13648. String off the disk. Depending on the system and the number of strings read,
  13649. the I/O delay might not be noticeable. With a disk cache, the delay might only
  13650. be apparent the first time a String was read.
  13651. If you are using NLS, Microsoft Windows, or the MacInstosh, the implementation
  13652. of this class could load the string via calls to the appropriate functions.
  13653. You would need to ensure that the values of the string identifiers matched on
  13654. all these systems, but that is a bookkeeping problem, not a coding problem.
  13655. Let's make a few changes and add a few features to this class:
  13656. class Collection_of_strings
  13657. {
  13658. public:
  13659. enum Retrieval_type {FAST_BUT_NEEDS_LOTS_OF_MEMORY,
  13660. SLOW_BUT_LITTLE_MEMORY};
  13661. enum Error_code {OK, IDENTIFIER_NOT_AVAILABLE,
  13662. LANGUAGE_NOT_AVAILABLE};
  13663. Collection_of_strings();
  13664. Error_code load(const char * identifier,
  13665. Retrieval_type retrieval_type,
  13666. Language language);
  13667. ~Collection_of_strings();
  13668. String get_string_by_index(unsigned int index);
  13669. ...
  13670. private:
  13671. String * strings;
  13672. unsigned int number_of_strings;
  13673. };
  13674. This class has a language selection capability, as well as the ability to
  13675. utilize alternative retrieval methods.
  13676. When you design a set of classes, you face a tradeoff between individual class
  13677. complexity and how many classes you must design. Sometimes using another
  13678. parameter in the constructor decreases the number of classes and simplifies
  13679. the class interface, although it may make the implementation slightly more
  13680. complex.
  13681. Instead of adding parameters to the constructor, I created a separate load
  13682. function. A constructor can only report problems via exceptions or by setting
  13683. a flag that can be tested later; having a separate load function allows an
  13684. error code to be returned. Since the likelihood is high that a requested
  13685. language won't be available, a return code is a better choice than an
  13686. exception as a reporting method.
  13687.  
  13688. The program source should contain a general language header file, say
  13689. "language.h" that contains:
  13690. enum Language {ENGLISH, SPANISH, FRENCH, ....};
  13691. The program may also require a file that contains strings with standard or
  13692. alternative spellings of languages for user interface programs. For example:
  13693. struct Language_equivalent
  13694. {
  13695. enum Language code;
  13696. char * string;
  13697. };
  13698.  
  13699. Language_equivalent language_equivalents[] =
  13700. {
  13701. {ENGLISH, "English"},
  13702. {ENGLISH, "American English"},
  13703. {GERMAN, "German"},
  13704. {GERMAN, "Deutsch"},
  13705. ...
  13706. };
  13707. With this Collection_of_strings class, the calling program might appear as
  13708. follows. (This example does not handle every possible error problem, but it
  13709. demonstrates the general approach.):
  13710. void main()
  13711. {
  13712. Collection_of_strings strings
  13713. Strings::Error_code error_code;
  13714. error_code = strings.load(MY_STRINGS_IDENTIFIER,
  13715. FAST_BUT_NEEDS_LOTS_ OF_MEMORY, FRENCH);
  13716. switch (error_code)
  13717. {
  13718. case OK:
  13719. break;
  13720. case IDENTIFIER_NOT_AVAILABLE:
  13721. cerr << "String data not available "
  13722. << MY_STRINGS_IDENTIFIER;
  13723. exit(1);
  13724. break;
  13725. case LANGUAGE_NOT_AVAILABLE:
  13726. cerr << "Language not available "
  13727. << "English selected";
  13728. error_code = strings.load(MY_STRINGS_IDENTIFIER,
  13729. FAST_BUT_NEEDS_LOTS_OF_MEMORY,
  13730. ENGLISH);
  13731. break;
  13732. }
  13733. ...
  13734. String a_string;
  13735. ...
  13736. a_string = strings.get_string_by_index(ERROR__STRING);
  13737. ...
  13738. cout << a_string;
  13739. // or whatever else you need to do with it
  13740. ...
  13741. }
  13742. You could implement this class with NLS fairly easily. Implementing it with
  13743. Microsoft Windows or the MacIntosh would be a bit more difficult, since the
  13744. resources, although logically separate from the code, are bound to the
  13745. executable file. With X-Window, you could load an .Xdefaults file based on the
  13746. particular human language being handled by the user interface.
  13747. A particular implementation might not be able to support efficiently both
  13748. Retrieval_types. You could add Error_codes to report that the desired type was
  13749. unavailable and an alternative type was employed. You might even try out
  13750. intermediate Retrieval_types, which could attempt to trade off memory space
  13751. for speed. However, that might unduly complicate the implementation.
  13752. Q
  13753. I have just read your comments on lint for C++ in C Users Journal with the
  13754. greatest interest as I was about to contact Gimpel with the same question that
  13755. Sue Lindsey posed to you.
  13756. I have found lint (with strong type checking) to be the single most valuable
  13757. tool and source of education in using C, and rely on it heavily. However, I've
  13758. been following the C++ and object-oriented programming literature and with a
  13759. current software project exceeding 10,000 lines of source code (for the first
  13760. time for me) I can readily appreciate some of the advantages C++ potentially
  13761. offers.
  13762. I don't have the luxury of much "dabbling time," and have been nervous about
  13763. making a difficult move which would also deprive me of my trusted lint! If
  13764. your statement that C++ implicitly takes care of most of the problem areas in
  13765. C that lint covers is really true, that would be one of the most powerful
  13766. reasons I have heard for moving from C to C++. I have never seen an equivalent
  13767. view expressed before, and I just want to hear you say it again!
  13768. Could I suggest that you expand on this a bit in a future column, perhaps even
  13769. seeking some input from Gimpel? I'm thinking of the number of times lint has
  13770. saved me from what would have been a hard bug to find by a "conceivably
  13771. un-initialized" warning in a rarely-taken program branch (for example). I
  13772. don't see anything in C++ that would help there; perhaps I'm wrong!
  13773. Finally, this gives me a welcome opportunity to thank you for your excellent
  13774. columns in CUJ -- I always read them first and, as a fairly isolated
  13775. practioner, find most OF them informative and entertaining.
  13776. Rob Sherlock
  13777.  
  13778. I described Gimpel's PC-lint for C++ in last month's column. If you were
  13779. hesitant about moving to C++ due to the lack of a lint for the language, you
  13780. need wait no longer.
  13781. As I noted in my answer to the original lint question, C++ compilers (as well
  13782. as many C compilers) have become more lint- like in their warning messages.
  13783. However, compilers still vary in recognizing potential errors. lint reports
  13784. conditions that are not detected by all compilers. For example, some compilers
  13785. may not recognize
  13786. if (a_variable=0)
  13787. another_variable=5;
  13788. as a potential error, but lint will.
  13789.  
  13790.  
  13791.  
  13792.  
  13793.  
  13794.  
  13795.  
  13796.  
  13797.  
  13798.  
  13799.  
  13800.  
  13801.  
  13802.  
  13803.  
  13804.  
  13805.  
  13806.  
  13807.  
  13808.  
  13809.  
  13810.  
  13811.  
  13812.  
  13813.  
  13814.  
  13815.  
  13816.  
  13817.  
  13818.  
  13819.  
  13820.  
  13821.  
  13822.  
  13823.  
  13824.  
  13825.  
  13826.  
  13827.  
  13828.  
  13829.  
  13830.  
  13831.  
  13832.  
  13833.  
  13834.  
  13835.  
  13836.  
  13837.  
  13838.  
  13839.  
  13840.  
  13841.  
  13842.  
  13843.  
  13844.  
  13845.  
  13846.  
  13847. Stepping Up To C++
  13848.  
  13849.  
  13850. Overloading and Overriding
  13851.  
  13852.  
  13853.  
  13854.  
  13855. Dan Saks
  13856.  
  13857.  
  13858. Dan Saks is the founder and principal of Saks & Associates, which offers
  13859. consulting and training in C++ and C. He is secretary of the ANSI and ISO C++
  13860. committees. Dan is coauthor of C++ Programming Guidelines, and codeveloper of
  13861. the Plum Hall Validation Suite for C++ (both with Thomas Plum). You can reach
  13862. him at 393 Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601,
  13863. or electronically at dsaks@wittenberg.edu.
  13864.  
  13865.  
  13866. In October, my wife Nancy suggested that our family should go to Disney World
  13867. for vacation in early December. I was reluctant because I thought I had too
  13868. much work to do. But that's been my excuse for weaseling out of a lot things
  13869. for the past few years. OK, I let her talk me into it. But I made her promise
  13870. to help me get caught up. And she did. And I almost did.
  13871. The C++ standards committee met in November and passed almost twice as many
  13872. motions as ever before at a single meeting. (I'll tell you about them in the
  13873. not-too-distant future.) So I had to spend an additional two or three days
  13874. that I hadn't planned on writing these longer-than-ever minutes. And all my
  13875. other work slipped.
  13876. So here I am now, sitting in my room at Disney's Carribean Beach Resort in
  13877. Orlando, overloaded with work, while Nancy, Ben and Jeremy are over riding on
  13878. the Big Thunder Mountain Railroad at the Magic Kingdom. I've got to hurry up
  13879. and write this so I can join them.
  13880. Many C++ programmers, even moderately experienced ones, confuse the terms
  13881. overloading and overriding. For beginners, overriding is often a new term Even
  13882. before they write their first line of C++, most programmers hear that C++
  13883. offers function and operator overloading. (It's one of the features that lures
  13884. them to C++ in the first place.) New C++ programmers don't encounter the term
  13885. overriding until they start using virtual functions. Then they confuse the
  13886. words simply because they sound alike. Beginning C++ programmers usually err
  13887. by speaking of overloading when they mean overriding, simply because they
  13888. don't understand the subtle differences.
  13889. Experienced programmers continue to confuse the terms because overloading and
  13890. overriding have similar properties. And of course, the words continue to sound
  13891. alike. It doesn't help when an overly-exuberent lecturer uses one word when he
  13892. or she means the other and no one in the audience catches the gaff. I know
  13893. that happened to me in some of my earliest presentations on C++. I don't
  13894. believe I've made that mistake recently, but I can't promise I'll never do it
  13895. again.
  13896. OK, so what's the difference? In short, overloading means declaring the same
  13897. symbol in the same scope to represent two or more different entities.
  13898. Overriding means redefining a name in a derived class to hide a name inherited
  13899. from a base class.
  13900. In C++, you can overload both function identifiers and operators. For example,
  13901. void put(FILE *f, char c);
  13902. void put(FILE *f, int i);
  13903. void put(FILE *f, const char *s);
  13904. overloads the identifier put by declaring three different functions with that
  13905. name. The predefined operator + is inherently overloaded because it already
  13906. applies to a variety of operand types. The declaration
  13907. int operator+(complex z1, complex z2);
  13908. overloads operator + to handle complex numbers as well. I described many of
  13909. the C++ overloading rules in detail in "Function Overloading," CUJ, November,
  13910. 1991, and in a four-part series on "Operator Overloading" that appeared in
  13911. every other CUJ from January through July, 1992.
  13912. The following code demonstrates overriding. Overriding applies specifically to
  13913. functions in class hierarchies. For example, given
  13914. class B
  13915. {
  13916. public:
  13917. virtual void f();
  13918. virtual int g(int i);
  13919. };
  13920. the definition
  13921. class D :
  13922. public B
  13923. {
  13924. public:
  13925. virtual void f();
  13926. };
  13927. derives class D from B and overrides B's f with D's own version of f. D does
  13928. not override g, so D's g is exactly as inherited from B.
  13929. Overloading and overriding interact in some complex and very subtle ways. By
  13930. itself, each feature is a powerful programming tool (maybe too powerful).
  13931. Together, these features produce much richer capabilities than most programs
  13932. need.
  13933. Overloading and overriding combine to produce some very puzzling diagnostic
  13934. messages, and even more baffling run-time errors. That's the nature of C++.
  13935. C++ provides sufficient capability for experienced programmers to write code
  13936. that is more maintainable, yet no less efficient, than it would be in C. But
  13937. in doing so, it gives naive programmers even more opportunities to get into
  13938. trouble (as if C doesn't already offer enough).
  13939. Some of the things I'm about to show you are pretty intricate. They are
  13940. probably more complex than anything you're likely to want to do in real C++
  13941. programs. My advice, as always, it to keep things as simple as you can. Then
  13942. why would I show you things you probably don't want to do? Because you're
  13943. likely to do them by accident. Understanding these examples should help you
  13944. recognize and correct your mistakes. And once in a great while, you might even
  13945. find a reason to do these things intentionally.
  13946. The following discussion of course assumes you're familiar with vtbls (virtual
  13947. tables) and vptrs (pointers to virtual tables) as an implementation model for
  13948. virtual functions in C++. I introduced them last month in "How Virtual
  13949. Functions Work" (CUJ, January, 1994).
  13950.  
  13951.  
  13952. Virtual and Non-virtual Overriding
  13953.  
  13954.  
  13955. A class can contain both virtual and non-virtual functions. For a given class,
  13956. the translator creates an entry in the vtbl only for each virtual function in
  13957. the class, not for the non-virtual functions.
  13958. A derived class can override any of its inherited functions, be they virtual
  13959. or not. When you override a function that's virtual in the base class, it
  13960. automatically becomes virtual in the derived class. You can't turn off the
  13961. dynamic binding when you override a virtual function. That is, you cannot
  13962. override a virtual function with a non-virtual function. On the other hand,
  13963. you can override a non-virtual function with either a virtual or a non-virtual
  13964. function. When you override a function that's non-virtual in the base class,
  13965. the overriding function is also non-virtual, unless declared so explicitly.
  13966. When a C++ translator first encounters the definition for a class D derived
  13967. from some base class B, it creates an image for D's vtbl by copying B's vtbl.
  13968. (As always, I am describing a conceptual model for how the translation works.
  13969. Any particular implementation may do it differently.) When it parses a
  13970. declaration in D that overrides a virtual function f, the translator simply
  13971. overwrites the entry for f in D's vtbl to point to D::f instead of B::f. Thus,
  13972. overriding a function that's virtual in B doesn't increase the size of D's
  13973. vtbl.
  13974. The translator resolves all non-virtual function calls during translation, so
  13975. it need not store any non-virtual function addresses in vtbls. Thus,
  13976. overriding a non-virtual function with another non-virtual function has no
  13977. effect on the vtbls at all. But, overriding a function that's non-virtual in B
  13978. with a virtual function in D increases the size of D's vtbl, adding a new
  13979. entry to D's vtbl that has no corresponding entry in B's vtbl.
  13980. Listing 1 shows a simple inheritance hierarchy that mixes virtual and
  13981. non-virtual overriding. The base class, B, has four member functions, but only
  13982. two are virtual. Thus, the D's vtbl has only two entries in it, for functions
  13983. f and h as shown in Figure 1.
  13984. Class C in Listing 1 derives from B and overrides three of the four functions
  13985. it inherits. During translation, C's vtbl starts out as a copy of B's. C::f is
  13986. virtual because it overrides virtual B::f, and the compiler replaces the first
  13987. entry in C's vtbl (corresponding to f) with the address of C::f. C::g
  13988. overrides non-virtual B::g. Since g's declaration in C doesn't include the
  13989. virtual specifier, C::g is also non-virtual. C doesn't override the h it
  13990. inherited from B, so the second entry in C's vtbl (corresponding to h)
  13991. continues to point to B::h.
  13992.  
  13993. C::j overrides non-virtual B::j, but C declares j as virtual. Therefore the
  13994. compiler adds a new entry at the end of C's vtbl corresponding to j, and fills
  13995. it in with the address of C::j. The resulting vtbl for class C also appears in
  13996. Figure 1.
  13997. Class D in Listing 2 derives in turn from C, and overrides functions h and j.
  13998. Both h and j are virtual in C, so they are also virtual in D. The translator
  13999. replaces the entries for h and j in D's vtbl with the addresses of D::h and
  14000. D::j, respectively. D's vtbl entry for f continues to point to C::f, as it did
  14001. in C's vtbl. See Figure 1 for D's completed vtbl.
  14002. Listing 2 contains a test program that illustrates the behavior of the
  14003. inheritance hierarchy defined in Listing 1. The statement
  14004. B *pb = &c;
  14005. assigns the address of c to pb, so that *pb has static type B but dynamic type
  14006. C. Thus, a non-virtual member function call applied to *pb selects a member
  14007. function from class B, but a virtual function call applied to *pb selects from
  14008. class C. You can resolve the non-virtual function calls merely by looking at
  14009. B's declaration in Listing 1. You resolve the virtual function calls by
  14010. looking at C's vtbl in Figure 1.
  14011. pb->g() and pb->j() call B::g and B::j, respectively because both functions
  14012. are non-virtual in B. pb->g() is straightforward because C's vtbl doesn't even
  14013. have an entry for g. pb->j() can be confusing because C's vtbl has an entry
  14014. for j. But the compiler always determines whether a call is virtual or
  14015. non-virtual based on the static type of the object. In this case, *pb has
  14016. static type B and j is non-virtual in B, so pb->j() ignores the vtbl and
  14017. simply calls B::j.
  14018. pb->f() calls C::f because f is virtual in B, *pb is a C object, and C
  14019. overrides f. Even though h is virtual in B, pb->h() still calls B::h. The call
  14020. goes through C's vtbl, but winds up at B::h anyway because C does not override
  14021. h.
  14022. I won't go over every call in Listing 2, but I will call your attention to the
  14023. calls, such as pc->B::f(), that explicitly qualify the function name with the
  14024. name of a base class. Again, *pc has static type C and f is virtual in C.
  14025. Without a qualified name, the call pc->f() behaves like a normal virtual
  14026. function call, selecting the function's address from the vtbl for D, because D
  14027. is the dynamic type of *pc. Looking in D's vtbl in Figure 1 you can see that
  14028. the entry for f points to C::f, so that's what gets called.
  14029. On the other hand, using an explicit base class name qualifier on the function
  14030. name turns off the virtual call mechanism and uses static binding. That is,
  14031. even though pc->f() is a virtual call, pc->B::f() is not. The call ignores the
  14032. dynamic type of *pc and invariably calls B::f. This rule exists so that a
  14033. virtual function in a derived class can call the function it overrides in a
  14034. base class without getting stuck in an infinite recursion. My article on
  14035. "Virtual Functions" (CUJ, December, 1993) explains this behavior in greater
  14036. detail, including a fairly practical example that relies on it.
  14037.  
  14038.  
  14039. Overriding Overloaded Functions
  14040.  
  14041.  
  14042. You can overload virtual functions. That is, you can declare more than one
  14043. virtual function with the same name in the same class, as in class stream
  14044. shown in Listing 3. As with any set of overloaded functions, each function
  14045. signature (the sequences of types in a formal parameter list) in an overloaded
  14046. set of virtuals must be sufficiently distinct for the compiler to tell them
  14047. apart. The vtbl for the class contains a distinct entry for each virtual
  14048. function name and signature. The vtbl for class stream appears in Figure 2. No
  14049. surprises so far.
  14050. Deriving from a base class with overloaded virtual functions behaves pretty
  14051. much as you'd expect, as long as you override all of the overloaded functions,
  14052. or none of them. But, if you derive from a base class with overloaded virtual
  14053. functions and override some, but not all, of those overloaded virtual
  14054. functions, the results may surprise you.
  14055. All the functions in a given set of overloaded functions must be declared in
  14056. the same scope. Another declaration for a function with the same name in an
  14057. inner scope doesn't add to the overloaded set; it starts a new set and
  14058. completely hides all of the overloaded functions of the outer scope while in
  14059. the inner scope. The inner scope can access the overloaded functions in the
  14060. outer scope only by explicitly using a :: (the scope resolution operator).
  14061. A class defines a new scope. The members of a class are in the scope of that
  14062. class. Thus, a single member function declaration in a class hides all the
  14063. overloaded functions with the same name declared in any enclosing scope, as
  14064. shown in Listing 4.
  14065. Listing 4 contains the definition for a class File, with a member function put
  14066. that writes a null-terminated string to a file. File::put uses one of the
  14067. overloaded put functions declared at file scope to actually put the string.
  14068. Unfortunately, none of those put functions at file scope are in scope inside
  14069. the body of File::put. Therefore, you must precede the call with :: to force
  14070. the compiler to look for a function at file scope, as shown in the body of
  14071. File::put in Listing 4. Otherwise, the C++ compiler thinks the call to put(s,
  14072. f) inside File::put is a (recursive) call to File::put, but with the wrong
  14073. number of arguments.
  14074. Similar behavior occurs if you derive class File from a base class that
  14075. contains several functions named put. The declaration for File::put hides all
  14076. the overloaded put functions in the base class while in the scope of class
  14077. File. A call to an inherited put function inside a File member function must
  14078. attach the base class name and a :: before the function name.
  14079. Now let's see what happens when a derived class overrides some, but not all,
  14080. of the overloaded virtuals in its base class. Listing 5 shows a base class B
  14081. with three virtual functions f(int), f(long) and f(char *). B's vtbl appears
  14082. in Figure 3.
  14083. Class C derived from B overrides only f(int). Therefore, only f(int) is
  14084. visible in the scope of C; however, C's vtbl still has three entries: one for
  14085. each virtual function in its base class. C's vtbl appears in Figure 3. It has
  14086. the same layout and values as B's vtbl, except the entry for f(int) points to
  14087. C::f(int) instead of B::f(int).
  14088. A derived class never has fewer virtual functions (i.e., a smaller vtbl) than
  14089. its base class. Some inherited virtual functions may be invisible in the
  14090. derived class scope, but their addresses must still be in the vtbl. Remember,
  14091. an object of a derived class is an object of its base class. A derived object
  14092. has everything that its base object has, and maybe more. This applies to vtbls
  14093. as well as the objects themselves. The vtbl for a derived class must have at
  14094. least as many entries as the vtbl for its base class. Consider the
  14095. consequences if this were not so.
  14096. The virtual call mechanism relies on the assumption that a derived object is a
  14097. base object. When a compiler encounters a virtual function call applied to an
  14098. object, it simply translates the call into code that follows a vptr to a vtbl
  14099. and selects the address of the appropriate function. The vtbl for the object
  14100. involved in the call must have at least as many entries as the vtbl for the
  14101. base class, or else the call might reach beyond the end of the object's vtbl
  14102. and grab something that isn't a function address.
  14103. Class D in Listing 5 derives from C and overrides only f(long). Again, its
  14104. vtbl has three entries, as shown in Figure 3. The values in D's vtbl as the
  14105. same as in C's, except for the value corresponding to f(long).
  14106. Listing 6 is a test program that demonstrates the behavior of the function
  14107. call bindings for the hierarchy in Listing 5. The first call contains no
  14108. surprises. *pb has static type B but dynamic type C. An expression that occurs
  14109. to the right of pb-> is in the scope of B. In the scope of B, the compiler can
  14110. choose from three different functions named f. f(1) exactly matchs f(int).
  14111. f(int) is virtual in B, so the compiler generates a virtual call to f(int). At
  14112. the time the program executes, pb actually points to a C object, and C
  14113. overrides f(int), so pb->f(1) calls C::f(int). The next two calls behave
  14114. similarly, except that C does not override f(long) and f(char *).
  14115. You may find the call d.f(1) surprising. It appears that f(1) matches f(int)
  14116. exactly, so at first it seems that it should call D::f(int). But D doesn't
  14117. override f(int), so shouldn't the expression actually call C::f(int)? Well,
  14118. that's not what happens either. The expression to the right of d. is in the
  14119. scope of D, where only f(long) is visible. Therefore the compiler promotes the
  14120. argument 1 to 1L and calls D::f(long).
  14121. You can study most of these calls on your own. I'll draw your attention to a
  14122. couple of interesting cases.
  14123. The call d.C::f(1L) uses explicit qualification to access an inherited
  14124. function that's otherwise hidden. It looks like it should call an f(long), but
  14125. it dosen't. The expression to the right of the qualifier C:: is in the scope
  14126. of C, where only f(int) is visible. The compiler converts 1L to 1 (an int) and
  14127. calls C::f(int). The explicit qualifier turns off the virtual call mechanism.
  14128. The call pc->f("hello") is an error. The expression to the right of pc-> is in
  14129. the scope of C, where only f(int) is visible. "hello" has type char *, and
  14130. there's no standard conversion from char * to int.
  14131.  
  14132.  
  14133. Behaving Responsibly
  14134.  
  14135.  
  14136. As I mentioned earlier, I'm not suggesting that you'd ever want to write code
  14137. like this. Quite to the contrary, I'm trying to shine a little light into a
  14138. dark corner, and show you how can inadvertantly write some pretty confounding
  14139. stuff.
  14140. I think overloaded virtual functions are a useful feature. Common classes like
  14141. the istream and ostream classes in iostream.h use this feature well. When you
  14142. drive from a class with a set of overloaded virtual functions, you should
  14143. override all or none of the functions in that set. In fact, this is a good
  14144. guidelines even if the overloaded functions are non-virtual.
  14145. Many compilers actually warn you when you violate this guideline. For example,
  14146. when you compile Listing 5 and Listing 6 together, you may get a warning to
  14147. the effect that the declarations of f(int) in C hides the declarations of
  14148. f(long)n and f(char *) inherited from B.
  14149. As with any guideline there are exceptions, but in this case they are rare.
  14150. Remember that function and operator loading are there to help you write more
  14151. intuitive code. If overloading makes it less so, then back off.
  14152. So, Dan Saks, you've just finished your CUJ article. What are you going to do
  14153. next?
  14154. Figure 1 The vtbls for the classes in Listing 1
  14155. Figure 2 vtbl for class stream in Listing 4
  14156. Figure 3 The vtbls for the classes in Listing 5
  14157.  
  14158. Listing 1 A class hierarchy that mixes virtual and non-virtual overriding
  14159. #include <iostream.h>
  14160.  
  14161. class B
  14162. {
  14163. public:
  14164. virtual void f(); // virtual
  14165. void g(); // non-virtual
  14166. virtual void h(); // virtual
  14167. void j(); //non-virtual
  14168. };
  14169. void B::f() { cout << "B::f()\n"; }
  14170.  
  14171. void B::g() { cout << "B::g()\n"; }
  14172.  
  14173.  
  14174. void B::h() { cout << "B::h()\n"; }
  14175.  
  14176. void B::j() { cout << "B::j()\n"; }
  14177.  
  14178. class C : public B
  14179. {
  14180. public:
  14181. void f(); // virtual
  14182. void g(); // non-virtual
  14183. virtual void j(); // virtual
  14184. };
  14185.  
  14186. void C::f() { cout << "C::f()\n"; }
  14187.  
  14188. void C::g() { cout << "C::g()\n"; }
  14189.  
  14190. void C::j() { cout << "C::j()\n"; }
  14191.  
  14192. class D: public C
  14193. {
  14194. public:
  14195. void h(); // virtual
  14196. void j(); // virtual
  14197. };
  14198.  
  14199. void D::h() { cout << "D::h()\n"; }
  14200.  
  14201. void D::j() { cout << "D::j()\n"; }
  14202.  
  14203. // End of File
  14204.  
  14205.  
  14206. Listing 2 A test program for the hierarchy in Listing 1
  14207. int main()
  14208. {
  14209. C, c;
  14210. D, d;
  14211.  
  14212. B* pb = &c; // ok, &c is a C * which is a B *
  14213. pb->f(); // calls C::f()
  14214. pb->g(); // calls B::g()
  14215. pb->h(); // calls B::h()
  14216. pb->j(); // calls B::j()
  14217.  
  14218. C *pc = &d; // ok, &d is a D * which is a C *
  14219. pc->f(); // calls C::f()
  14220. pc->B::f(); // calls B::f()
  14221. pc->g(); // calls C::g()
  14222. pc->h(); // calls D::h()
  14223. pc->C::h(); // calls B::h()
  14224. pc->j(); // calls D::j()
  14225. pc->C::j(); // calls C::j()
  14226.  
  14227. B &rb= *pc; // ok, *pc is a C which is a B
  14228. rb.f(); // calls C::f()
  14229. rb.B::f(); // calls B::f()
  14230. rb.g(); // calls B::g()
  14231. rb.h(); // calls D::h()
  14232. rb.j(); // calls B::j()
  14233.  
  14234.  
  14235. return 0;
  14236. }
  14237.  
  14238. // End of File
  14239.  
  14240.  
  14241. Listing 3 A class with overloaded virtual functions
  14242. class stream
  14243. {
  14244. // ...
  14245. public:
  14246. virtual stream &get(char &c);
  14247. virtual stream &get(double &d);
  14248. virtual stream &get(char *s);
  14249. virtual stream &put(char c);
  14250. virtual stream &put(double d);
  14251. virtual stream &put(const char *s);
  14252. // ...
  14253. };
  14254.  
  14255. // End of File
  14256.  
  14257.  
  14258. Listing 4 A declaration in an inner scope hides all functions with the same
  14259. name in an outer scope
  14260. #include <stdio.h>
  14261.  
  14262. void put(char c, FILE *stream);
  14263. void put(const char *s, FILE *stream);
  14264.  
  14265. class File
  14266. {
  14267. FILE *f;
  14268. public:
  14269. File(FILE *ff) : f(ff) { }
  14270. void put(const char *s);
  14271. };
  14272.  
  14273. void File::put(const char *s)
  14274. {
  14275. ::put(s, f); // needs :: to
  14276. // access outer scope
  14277. }
  14278.  
  14279. int main()
  14280. {
  14281. File f(stdout);
  14282. f.put("hello, world\n");
  14283. return 0;
  14284. }
  14285.  
  14286. // End of File
  14287.  
  14288.  
  14289. Listing 5 Aa class hierarchy that overrides some but not all overloaded
  14290. virtual functions
  14291. #include <iostream.h>
  14292.  
  14293. class B
  14294. {
  14295.  
  14296. public:
  14297. virtual void f(int i);
  14298. virtual void f(long L);
  14299. virtual void f(char *up);
  14300. };
  14301.  
  14302. void B::f(int i)
  14303. {
  14304. cout << "B::f(int i = << i << ")\n";
  14305. }
  14306.  
  14307. void B::f(long L)
  14308. {
  14309. cout << "B::f(long L) = " << L <<")\n";
  14310. }
  14311.  
  14312. void B::f(char *p)
  14313. {
  14314. cout << "B::char *p = \"" << p << "\")\n";
  14315. }
  14316.  
  14317. class C : public B
  14318. {
  14319. public:
  14320. void f(int i); // virtual
  14321. };
  14322.  
  14323. void C::f(int i)
  14324. {
  14325. cout << "C::f(int i = "<< i<<")\n";
  14326. }
  14327.  
  14328. class D : public C
  14329. {
  14330. public:
  14331. void f(long L); // virtual
  14332. };
  14333.  
  14334. void D::f(long L)
  14335. {
  14336. cout << "D::f(long L = "<< L <<")\n";
  14337. }
  14338.  
  14339. // End of file
  14340.  
  14341.  
  14342. Listing 6 A test program for the hierarchy in Listing 5.
  14343. int main()
  14344. {
  14345. C c;
  14346. D d;
  14347.  
  14348. B *pb = &c; // ok, &c is a C * which is a B*
  14349. pb->f(1); // calls C::f(int)
  14350. pb->f(2L); // calls B::f(long)
  14351. pb->f("hello"); // calls B::f(char *)
  14352.  
  14353. d.f(1); // calls D::f(long)
  14354. // C::f(int) is hidden
  14355.  
  14356. // but 1 promotes to long
  14357. d.C::f(1); // calls C::f(int)
  14358. d.B::f(1); // calls B::f(int)
  14359. d.f(1L); // calls D::f(long)
  14360. d.C::f(1L); // calls C::f(int)
  14361. // B::f(long) is hidden
  14362. // but 1L converts to int
  14363. d.B::f(1L); // calls B::f(long)
  14364.  
  14365. C *pc = &d; // ok, &d is a D * which is a C*
  14366. pc->f(1); // calls C::f(int)
  14367. pc->f(1L) // calls C::f(int)
  14368. // B::f(long)is hidden
  14369. // but 1L converts to int
  14370. pc->B::f(1L); // calls B::f(long)
  14371. pc->f("hello"); // error! f(char *) is hidden
  14372. pc->B::f("hello"); // calls B::f(char *)
  14373.  
  14374. B &rb = *pc; // ok, *pc is a C which is a B
  14375. rb.f(1); // calls C::f(int)
  14376. rb.f(2L); // calls D::f(long)
  14377. rb.B::f(2L); // calls B::f(long)
  14378. rb.f("hello"); // calls B::f(char *)
  14379.  
  14380. return 0;
  14381. }
  14382.  
  14383. // End of File
  14384.  
  14385.  
  14386.  
  14387.  
  14388.  
  14389.  
  14390.  
  14391.  
  14392.  
  14393.  
  14394.  
  14395.  
  14396.  
  14397.  
  14398.  
  14399.  
  14400.  
  14401.  
  14402.  
  14403.  
  14404.  
  14405.  
  14406.  
  14407.  
  14408.  
  14409.  
  14410.  
  14411.  
  14412.  
  14413.  
  14414.  
  14415.  
  14416.  
  14417.  
  14418.  
  14419. On the Networks
  14420.  
  14421.  
  14422. Concurrent Development
  14423.  
  14424.  
  14425.  
  14426.  
  14427. Sydney S. Weinstein
  14428.  
  14429.  
  14430. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, lecturer, author,
  14431. professor, and President of Myxa Corporation, a consulting and contract
  14432. programming firm specializing in databases, data presentation and windowing,
  14433. transaction processing, networking, testing and test suites, and device
  14434. management for UNIX and MS-DOS. He can be contacted care of Myxa Corporation,
  14435. Inc., 3837 Byron Road, Huntingdon Valley, PA 19006-2320 or via electronic mail
  14436. on the Internet/USENET mailbox syd@myxa.COM (dsinc!syd for those who cannot do
  14437. Internet addressing).
  14438.  
  14439.  
  14440. For years, UNIX developers have used SCCS and RCS to control source code and
  14441. deal with tracking changes. But users of these two programs always have to
  14442. deal with a potential problem. If more than one developer is working on a
  14443. project, there is always the chance that the their efforts will collide. This
  14444. collision occurs when the two developers both try to update the same file at
  14445. the same time.
  14446. RCS and SCCS solve this problem by allowing one of the developers to lock on
  14447. file for editing. In doing so, the person who locks the file is the only one
  14448. who can make changes to the file. This scheme works well, but leads to another
  14449. problem, deadlock. Popular files get locked by almost everyone; and of course,
  14450. with the one-lock-at-a-time method, the other developers must wait until the
  14451. first developer is done with it. Those who miss the chance to grab a file may
  14452. have to wait a while as its current holders develop their changes, integrate
  14453. and test them, make them permanent, and release the lock.
  14454. Peter Miller <pmiller@bmr.gov.au> provides a possible solution, aegis-2.1,
  14455. posted as Volume 27, Issues 36-54 of comp. sources. unix. Quoting Peter:
  14456. "The aegis program is a CASE tool with a difference. In the spirit of the UNIX
  14457. Operating System, the aegis program is a small component designed to work with
  14458. other programs."
  14459. aegis is a project change supervisor. It provides a framework within which a
  14460. team of developers may work on many changes to a program independently. aegis
  14461. coordinates the integration of these changes back into the master source of
  14462. the program, with as little disruption as possible. Resolution of contention
  14463. for source files, a major headache in any project with more than one
  14464. developer, is one of the aegis's major functions.
  14465. aegis uses a software development model consisting of a project master source
  14466. (or baseline) of a project, and a team of developers creating changes to be
  14467. made to this baseline. When a change is complete, aegis integrates it with the
  14468. baseline, to create a new baseline. aegis requires each change to be atomic
  14469. and self-contained, and allows no change to cause the baseline to cease
  14470. working. "Working" for a baseline is defined as passing its own tests. The
  14471. tests are considered part of the baseline.
  14472. To ensure that changes can't make the baseline stop working, aegis mandates
  14473. that changes be accompanied by at least one test, and that all such tests
  14474. complete successfully. These steadily accumulated tests form an ever
  14475. increasing regression test suite for all later changes. aegis also mandates a
  14476. review stage for each change to the baseline.
  14477. One benefit in using aegis is that there are only O(n) interactions between
  14478. developers and the baseline. Contrast this number with the number of
  14479. interactions for a master source that is being edited directly by the
  14480. developers -- there are O(n!) interactions between developers, which makes
  14481. adding "just one more" developer a potential disaster. Another benefit of
  14482. aegis is that the project baseline always works. Always having a working
  14483. baseline lets you always have a version available for demonstrations, or those
  14484. "pre-release snapshots" developers are always forced to provide.
  14485. aegis is often compared to CVS, the RCS tool for handling concurrent
  14486. development. In effect, aegis adds baseline validity checks to CVS, since
  14487. aegis requires a change to pass validity checks before it is added to the
  14488. baseline.
  14489.  
  14490.  
  14491. More for UNIX
  14492.  
  14493.  
  14494. Continuing with the highlights this month from comp.sources.unix: Gordon Ross
  14495. <gwr@mc.com> extended bootp-2.2. B, posted as Volume 27, Issues 63 and 64.
  14496. BOOTP is a server for booting systems over the network. New in this version is
  14497. support for clients that need the server to honor the format of the options
  14498. area, and support for an extended option area. In addition the source now
  14499. works with SVR4 systems. A patch was issued in Volume 27, Issue 76.
  14500. In addition, Gordon submitted a test program for exercising BOOTP servers.
  14501. This program appears as Volume 27, Issue 65 and can be used to debug problems
  14502. encountered when developing or using BOOTP servers.
  14503. Uwe Doering <fas@geminix.inberlin.de> contributed an important re-release of
  14504. FAS as fas-2.11.0, in Volume 27, Issues 67-74. The re-release contains an
  14505. important change which affects users of FAS on SCO UNIX. This change fixes a
  14506. bug in FAS which caused reliability problems (crashes) with SCO UNIX. Other
  14507. changes in this new version include performance improvements, bug fixes, more
  14508. options on hardware flow control, including DSR as well as CTS lines, and
  14509. enhanced support for 57,600 and 115,200 bps modes.
  14510. Much discussion has occurred on the net on how to verify that a file is the
  14511. original file and is unmodified, especially with the often "lossy" connections
  14512. used to transfer files around the network. Checksum programs can help verify a
  14513. file's originality -- several checksum programs which have been developed over
  14514. the years include MD5, Snefru-8, and the traditional CRC-32. Daniel J.
  14515. Bernstein <djb@silverton.berkeley.edu> has combined all of these into a single
  14516. package, fingerprint, which he submitted for Volume 27, Issues 79 and 80.
  14517. fingerprint provides a common set of programs and C-callable libraries for
  14518. producing a universal fingerprint of a file.
  14519. If you find Make limited in what it can do, and would prefer a full language
  14520. for makefiles complete with conditionals, loops, and all those other features
  14521. you expect in a full language, consider jam from Christopher Seiwald
  14522. <seiwald@vix.com>. jam is a make-like program that adds full language
  14523. features, plus a centralized database for project-wide rules, as well as
  14524. several other extensions. jam was posted as Volume 27, Issues 81-85.
  14525. Wayne Davison <davison@borland.com> contributed mthreads, a news database
  14526. manager that processes one or more newsgroups into a tree-structured list of
  14527. articles related by their References and Subject lines. For each group you
  14528. enable, mthreads produces a .thread file that trn (Threaded Read News) can
  14529. read to get a quick summary of the articles in the group and how they are
  14530. related. mthreads takes up about three to five percent of your news-spool if
  14531. you enable all groups. (Any site which is not running INN must run mthreads to
  14532. use trn-3.3.) mthreads is posted as Volume 27, Issues 90-93.
  14533. Wayne followed this posting with a new release of Threaded RN, trn-3.3, as
  14534. Issue 27, Volumes 94-105. trn is a newsreader that uses an article's
  14535. references to display discussions in a natural reply-ordered sequence called
  14536. threads. New in 3.3 is support for a default subscription list for new users,
  14537. better handling of redirected and disabled groups, support for MIME, and
  14538. various bug fixes.
  14539. The collection of useful libraries grew once again with the contribution of
  14540. clc by Panos Tsirigotis <panos@anchor.cs.colorado.edu> posted as Volume 27,
  14541. Issues 106-126. The C Libraries Collection includes the following:
  14542. dict. Support for various types of data structures, including doubly-linked
  14543. lists, hash tables and binary search trees.
  14544. fsma. Support for quick memory allocation/deallocation of fixed-size objects.
  14545. misc. A collection of generic functions including functions for managemeat of
  14546. environment variables, a tree walk function to replace ftw(3), and functions
  14547. to get the basename/dirname of a pathname.
  14548. pq. An implementation of priority queues using heaps.
  14549. pset. Support for pointer sets, implemented as dynamic pointer arrays.
  14550. sio. Support for fast stream I/O, optionally using memory mapping for input if
  14551. the operating system supports it. sio din includes four types of functions:
  14552. string matching functions (offering the Boyer-Moore, Knuth-Morris-Pratt,
  14553. Rabin-Karp, and Shift-OR algorithms), string printing functions, string
  14554. parsing functions, and string utility functions.
  14555. timer. Support for multiple timers by multiplexing the timers provided by the
  14556. operating system.
  14557. xlog. This library provides logging objects which can be connected either to
  14558. syslog or to a file. Objects connected to files may be customized to not
  14559. exceed a certain file size.
  14560. David I. Bell <dbell@canb.auug.org.au> contributed a new release of
  14561. calc-2.9.0, posted as Volume 27, Issues 127- 146. calc is an arbitrary
  14562. precision C-like programmable calculator with many built-in functions. calc's
  14563. basic data types are integers, fractions, complex numbers, strings, matrices,
  14564. associations, lists, files, and user-definable "objects." You can use calc
  14565. interactively to evaluate expressions line by line, or you can write
  14566. complicated programs in its C-like language. New in this version is ANSI C
  14567. support, initialization of new objects and variables to zero instead of null,
  14568. addition of static variable support, and many bug fixes.
  14569.  
  14570.  
  14571. Plot your MISC
  14572.  
  14573.  
  14574. The major update in comp.sources.misc is version 3.5 of gnuplot from Alexander
  14575. Woo <woo@playfair. stanford.edu>. Posted as Volume 40, Issues 13-45, gnuplot
  14576. is a command-line driven interactive function plotting utility for UNIX,
  14577. MSDOS, and VMS platforms. gnuplot was originally intended as a graphical
  14578. program to allow scientists and students to visualize mathematical functions
  14579. and data. Additions to this version of the software allow plots of
  14580. three-dimensional functions and data files. gnuplot supports many different
  14581. types of terminals, plotters, and printers and is easily extensible to include
  14582. new devices. gnuplot handles both 2-D and 3-D coordinate spaces and objects.
  14583. This release marks the end of Alexander's tenure as the coordinator of the
  14584. gnuplot effort. He has passed the hat on to Alexander Lehmann, who has
  14585. volunteered to coordinate the next release. Send all new contributions to
  14586. bug-gnuplot@ dartmouth.edu.
  14587. Angus Duggan <ajcd@dcs.ed.ac.uk> has submitted an update to psutils posted as
  14588. Volume 39, Issues 93-96. This package is a set of utilities for manipulating
  14589. PostScript documents. This version supports page selection and rearrangement,
  14590. including arrangement into signatures for booklet printing, and page merging
  14591. for n-up printing. In addition, this version includes several shell and perl
  14592. scripts for converting many postscript output formats into the standard paging
  14593. conventions so psutils can process them. A patch appeared in Volume 41, Issue
  14594. 29 to add several new features and regularize several options across the
  14595. utilities.
  14596. Michael Peppler <mpeppler@itf.ch> re-released Sybperl at patch level 8 as
  14597. Volume 39 Issues 101-103. Sybperl is a set of extensions to perl to add direct
  14598. accesses to sybase databases via the dblibrary. Additions in this level
  14599. include a user settable variable to control null return on queries and to
  14600. control how binary data is formatted. The new patch also adds several internal
  14601. calls to avoid packaging problems, and access to the bulk copy calls in
  14602. dblibrary. Patch 9 was posted in Volume 40, Issue 5; it can set the
  14603. application name for the sybase process and it fixes some bugs.
  14604. Ted A. Campbell <tcamp@delphi.com> has contributed bwbasic, Bywater BASIC
  14605. Interpreter/Shell, version 2.10, as Volume 40, issues 52-54. bwbasic
  14606. implements a large superset of the ANSI Standard for Minimal BASIC
  14607. (X3.60-1978) and a significant subset of the ANSI Standard for Full BASIC
  14608. (X3.113-1987) in C. bwbasic also offers shell programming facilities as an
  14609. extension of BASIC. New features in this version are compatibility with K&R C
  14610. compilers, implementation of ANSI-BASIC-style structured programming,
  14611. enhancements to the interactive environment, bug fixes, and portability
  14612. enhancements.
  14613. Mike Gleason <mgleason@cse.unl.edu> contributed a new release of ncftp as
  14614. Volume 40, Issues 76-81. ncftp is an alternative user interface for the ftp
  14615. utility, ncftp offers a much more friendly and convient interface than
  14616. standard ftp programs. New features in 1.6 includes support for the term
  14617. package used by Linux, support for SCO Xenix, AIX and Dynix/PTX, incorporation
  14618. of all the fixes from patches, and then some.
  14619. Adam Costello <amc@wuecl.wustl.edu> contributed an improved paragraph
  14620. formatter, par, as Volume 38, Issues 114-116. par is a filter which reformats
  14621. each paragraph as it copies its input to its output. par generates each output
  14622. paragraph from the corresponding input paragraph according to the following
  14623. rules: par removes an optional prefix and/or suffix from each input line,
  14624. divides the remainder of the line into words (delimited by spaces), joins the
  14625. words into lines to make an eye-pleasing paragraph, then reattaches the
  14626. prefixes and suffixes. If a line contains suffixes, par inserts spaces before
  14627. them so that they all end in the same column. par provides many other options
  14628. to control paragraph formatting. par's main benefit over fmt is its support
  14629. for prefix and suffix characters, and its allowance of leading indention.
  14630. Panos Tsirigotis <panos@cs.colorado.edu> contributed pst, a program which
  14631. extracts the text of a PostScript file and which tries to make that text
  14632. appear as it does in the PostScript document. pst is posted as Volume 40,
  14633. Issues 172-177. pst tries to avoid splitting words and to maintain paragraph
  14634. boundaries. To achieve these goals, pst first tries to identify how the
  14635. PostScript file was produced, so that it can use an appropriate text
  14636. extraction algorithm. The user may also specify which of the available
  14637. algorithms to use to extract text.
  14638.  
  14639. On the patch front, remind received some additions via Patches 9 (Volume 39,
  14640. Issues 115-118), 10 (Volume 40, Issues 48-50), and 11 (Volume 40, Issue
  14641. 167-171). New features in patch 9 include display of moon phases (including
  14642. little moon symbols in the PostScript calendar), ANSI color terminal support,
  14643. flexibility for Sunday or Monday as start of week, better control of
  14644. PostScript calendar printouts, plus the usual bug fixes. Patch 10 added 0S/2
  14645. support, more sorting options, MS-DOS and OS/2 test suites, and more
  14646. PostScript tuning. Patch 11 adds release notes for UNIX and OS/2 for pop-up
  14647. alarms, improvements for OS/2 and some optimizations, plus bug fixes.
  14648. Aphael Manfredi <ram@acri.fr> issued a few more patches to dist-3.0 to handle
  14649. more updates to the module library. Patch 12-13 (Volume 40, Issue 46-47)
  14650. updated dist to use a never version of itself, and added many changes to the
  14651. modules based on feedback from developers using the disk package. Patch 14
  14652. (Volume 40, Issue 128) fixes some bugs and adds some consistency checking.
  14653. The C++ libraries for parsing UNIX-like command line options and arguments
  14654. from Brad Appleton <brad@amber.ssd.csd.harris.com> received patches. Patch 2,
  14655. for options, was posted as Volume 40, Issue 157. Patch 2 added hidden options
  14656. and fixed some portability problems. Patch 3 for cmdline documented secret
  14657. arguments to cmdparse, and fixed some bugs.
  14658.  
  14659.  
  14660. Heavy Rolodexs
  14661.  
  14662.  
  14663. Though they're not too portable, and a bit heavy, computers make good
  14664. rolodexs. Gregg Hanna <gregor@kafka.saic.com> has contributed as Volume 21,
  14665. Issues 810, mrolo, to comp.sources.x. mrolo is a set of card file database
  14666. programs designed to be simple yet robust, and was born out of frustration
  14667. with xrolo's and xrolodex's many menus. The main program is mrolo, the Motif
  14668. based card file program; the contribution also includes crolo, the
  14669. curses-based equivalent. Some of mrolo's features include:
  14670. From the main screen you see a list of all cards and may scroll through them
  14671. viewing/editing cards as desired. You may jump to a section of the card file
  14672. by clicking on a lettered tab (A-Z) on the edge of the screen (implemented in
  14673. mrolo) or by typing the upper-case letter (implemented in crolo).
  14674. You can search cards quickly by simply entering text in a text field on the
  14675. main screen. (You can easily do a regular expression search, if you want, just
  14676. start the text with a slash.)
  14677. There are no explicit save/load operations, the current display reflects the
  14678. disk file's contents and mrolo verifies and writes changes at the time of the
  14679. change.
  14680. New features in version 1.3 include regular expression matching, addition of
  14681. crolo (curses version) for dial-up use, an "as of" feature for tracking card
  14682. dating, filter constraints, and the usual bug fixes. Two patches appeared,
  14683. Patch 1 in Volume 21, Issue 43 and patch 2 in Volume 44. Both are bug fix
  14684. patches.
  14685. George Ferguson <ferguson@cs.rochester.edu> additionally, published two
  14686. patches to xarchie. Patch 2 (Volume 21, Issue 1) is labeled internally patch
  14687. 8, and brings xarchie to 2.0.8. This patch fixes a minor bug in the X routines
  14688. and adds additional server information. Patch 3 (Volume 21, Issue 7) takes
  14689. xarchie to 2.0.9, adds more server info and fixes a misunderstanding in the
  14690. FTP cwd command.
  14691. Robert Andrew Ryan <rr2b+@andrew.cmu.edu> contributed sxpc, a program to
  14692. compress the X protocol stream, as Volume 21, Issue 12. sxpc provides up to
  14693. 75% compression of the X protocol stream. sxpc's intended use is to improve
  14694. the performance of X applications over a slow internet connection (e.g. slip,
  14695. cslip, or term). sxpc assumes there is a Unix operating system at both ends of
  14696. the link.
  14697. Brian V. Smith <envbvs@epb12.lbl.gov> contributed patch 2 to xfig as Volume
  14698. 21, Issues 21-36. xfig is an X program to draw and manipulate objects. New
  14699. features include changes in minimum movements, display of lengths, not just
  14700. resize/move on highlight, automatic use of scalable fonts if available, on
  14701. screen rotation of text, double click support on more menu items, keyboard
  14702. accelerators, and many bug fixes.
  14703. Yearning for the "good old days" when outputting simple graphs didn't require
  14704. so much window and event management code, Antoon Demaree <demaree@imec.be>
  14705. contributed xvig, X Window Virtual Graphics as Volume 21, Issues 48-57. xvig
  14706. is a simple graphics library that is I/O driven and supports opening windows,
  14707. drawing shapes and text, and dealing with cursors. xvig produces simple graphs
  14708. without scroll bars, pop-up menus, and fancy text features.
  14709.  
  14710.  
  14711. Previews from alt.sources
  14712.  
  14713.  
  14714. As usual, there's plenty in alt.sources so here are just a couple of
  14715. highlights of what's to come in the mainstream groups.
  14716. In a blast from the far past, James Hightower <jamesh@netcom.com> posted
  14717. focal, a FOCAL interpreter. He didn't write it, and is interested in who did,
  14718. but he posted it. It's from 1981, and provides a version of the FOCAL language
  14719. as used to run on the old PDP-8 computers. focal was posted on October 16,
  14720. 1993 in one part. Jonathan A. Chandross <jac@pilot.njin.net> saw it, tried to
  14721. compile it, and failed, and re-posted a corrected version. Chandross also
  14722. added a makefile, documentation, and an example FOCAL program. He posted his
  14723. corrected version on October 22, 1993, also in one part.
  14724. Thomas Driemeyer <thomas@bitrot.inberlin.de> posted a schedule planner based
  14725. on X/Motif in 11 parts on November 21, 1993. plan displays a month calendar
  14726. similar to xcal, but its every-day box is large enough to show appointments in
  14727. small print. By pressing on a day box, you can list and edit the appointments
  14728. for that day. plan also supports warn and alarm times and several methods of
  14729. listing appointments.
  14730.  
  14731.  
  14732.  
  14733.  
  14734.  
  14735.  
  14736.  
  14737.  
  14738.  
  14739.  
  14740.  
  14741.  
  14742.  
  14743.  
  14744.  
  14745.  
  14746.  
  14747.  
  14748.  
  14749.  
  14750.  
  14751.  
  14752.  
  14753.  
  14754.  
  14755.  
  14756.  
  14757.  
  14758.  
  14759.  
  14760.  
  14761.  
  14762.  
  14763.  
  14764.  
  14765.  
  14766.  
  14767.  
  14768. Code Capsules
  14769.  
  14770.  
  14771. Variable-Length Argument Lists
  14772.  
  14773.  
  14774.  
  14775.  
  14776. Chuck Allison
  14777.  
  14778.  
  14779. Chuck Allison is a regular columnist with CUJ and a software architect for the
  14780. Family History Department of the Church of Jesus Christ of Latter Day Saints
  14781. Church Headquarters in Salt Lake City. He has a B.S. and M.S. in mathematics,
  14782. has been programming since 1975, and has been teaching and developing in C
  14783. since 1984. His current interest is object-oriented technology and education.
  14784. He is a member of X3J16, the ANSI C++ Standards Committee. Chuck can be
  14785. reached on the Internet at allison@decus.org, or at (801)240-4510.
  14786.  
  14787.  
  14788. If printf is not the most widely used function in the standard C library, it
  14789. is certainly the most flexible. printf, and its companions sprintf and
  14790. fprintf, are the workhorses of formatted output. Their ability to process a
  14791. variable number of arguments makes these functions very useful indeed. The
  14792. format string in the statement
  14793. printf("%d, %s\n",n,s);
  14794. tells printf that it must extract an int and then a pointer to char from the
  14795. argument space (usually the program stack). But how does printf get at the
  14796. optional arguments? Well, I can't show you the inner workings of printf, but I
  14797. can show you the mechanism C provides to handle variable-length argument
  14798. lists. In this article I will show how you can write functions of your own
  14799. that accept a variable number of arguments, and why you might want to do so.
  14800.  
  14801.  
  14802. The Ellipsis Prototype Specification
  14803.  
  14804.  
  14805. printf's function prototype in your compiler's stdio.h should look something
  14806. like
  14807. int printf(const char *, ...);
  14808. The ellipsis tells the compiler to allow zero or more arguments of any type to
  14809. follow the first argument in a call to printf. The format string communicates
  14810. the number and type of the caller's optional arguments to the printf function.
  14811. For printf to behave correctly, the arguments that follow the format string
  14812. must match the types of the corresponding edit descriptors in the format
  14813. string. If the argument list contains fewer arguments than the format string
  14814. expects, the results are undefined. If the argument list contains more
  14815. arguments than expected, printf ignores them. The bottom line is that when you
  14816. use the ellipsis in a function prototype you are telling the compiler not to
  14817. type-check your optional arguments because you think you know what you're
  14818. doing -- so be sure that you do.
  14819.  
  14820.  
  14821. Variable-Length Argument Lists from Scratch
  14822.  
  14823.  
  14824. It's not too difficult to write your own functions that will accept a variable
  14825. number of arguments. The program in Listing 1 extracts the maximum of a list
  14826. of integers. It uses a fixed integer argument to communicate the number of
  14827. elements in the list. The program assumes that the list begins immediately
  14828. after the integer n in memory, that is, at address &n + 1.
  14829. The program in Listing 2 extends this technique to lists of mixed types. Since
  14830. the pointer p must visit arguments of different size, I have defined it to be
  14831. a pointer to char. To extract an object of a certain type from the argument
  14832. space, I just apply the appropriate cast and dereference, as in
  14833. s = *(char **) p;
  14834. and I use pointer arithmetic to skip over the extracted object:
  14835. p + = sizeof s;
  14836. The following macros, which automate this process, appear in Listing 3:
  14837. #define first_arg(x,p)
  14838. p = (char *) &x + sizeof(x)
  14839. #define next_arg(p,T,x)
  14840. x = *(T*)p; p += sizeof(T)
  14841. first_arg initializes p with the address of the first argument that follows
  14842. the fixed argument x. next_arg assigns an object of type T to x and then skips
  14843. past it.
  14844.  
  14845.  
  14846. The va_list Mechanism
  14847.  
  14848.  
  14849. The programs in Listing 1 through Listing 3 are okay for illustration
  14850. purposes, but they aren't portable; they only work on platforms that store
  14851. arguments linearly in order of increasing address and that leave no holes
  14852. between arguments. Fortunately, Standard C defines a portable method for
  14853. processing variable-length argument lists, with the macros defined in stdarg.h
  14854. (see Listing 4). The stdarg header defines a new type, va_list ("variable
  14855. argument list"), which refers to the list of optional, trailing arguments in a
  14856. function call. The statement
  14857. va_start(args,npairs);
  14858. initializes args to point to the va_list adjacent to the fixed argument
  14859. npairs. To extract an object from the list and "advance" to the next, call
  14860. va_arg, specifying the desired type:
  14861. n = va_arg(args,int);
  14862. To be completely portable, you must close a va_list with the va_end macro
  14863. (although on my compiler it is just a no-op). Listing 5 shows how to code a
  14864. portable version of the maxn function from Listing 1.
  14865. As you can see, to use variable-length argument lists you must provide two
  14866. things:
  14867. 1) At least one fixed argument (always the last before the ellipsis) to
  14868. initialize the va_list, and
  14869. 2) Some mechanism that communicates the number and/or type of arguments to the
  14870. function.
  14871. The following function prototype is both useless and syntactically invalid:
  14872. void f(...);
  14873. /* Location of args unknown */
  14874.  
  14875. There are a number of ways to satisfy point 2). For example, the program in
  14876. Listing 6 concatenates a variable number of strings into its fixed string
  14877. argument. The program processes one string after another until it finds a null
  14878. pointer in the va_list. A call such as
  14879. concat(s,NULL);
  14880. initializes s to the empty string.
  14881. As another example, consider a certain screen interface library that supports
  14882. data entry tables. The library has a function table_put_row that allows you to
  14883. fill rows with initial data. For example, if you have defined the columns of a
  14884. table to represent name, occupation and salary fields, you can populate the
  14885. table like this:
  14886. table_put_row(tp,0,"Sandra",
  14887. "Executive","57000");
  14888. table_put_row(tp,1,"James",
  14889. "Mechanic","45000");
  14890. table_put_row(tp,2,"Kimberly",
  14891. "Musician","66000");
  14892. /* etc. */
  14893. where tp is a pointer to a table structure and the second argument is the row
  14894. number. As you can see in Listing 7, table_put_row doesn't need a parameter
  14895. specifying the number of field arguments since it can infer that number
  14896. directly from the Table structure.
  14897.  
  14898.  
  14899. va_lists as Arguments
  14900.  
  14901.  
  14902. Listing 8 presents a useful function, fatal, which prints a formatted message
  14903. to stderr and exits a program gracefully. You call it as you would printf,
  14904. with a format string and a list of parameters, such as
  14905. fatal("Error %d on device %d\n",
  14906. err,dev);
  14907. What you would like to do is just pass the format string and print arguments
  14908. to some function that implements the printf machinery. The C library function
  14909. vfprintf makes this very easy. All you have to do is initialize a va_list with
  14910. the print arguments and pass it as the third argument. As you would expect,
  14911. the C library includes the companion functions vprintf and vsprintf as well.
  14912.  
  14913.  
  14914. Why the Fuss?
  14915.  
  14916.  
  14917. Programmers coming from most any other language may wonder why we need all of
  14918. this machinery. For example, the FORTRAN programmer is quite accustomed to
  14919. writing print statements that give no explicit information as to number or
  14920. type of arguments in its argument list:
  14921. * Output two numbers:
  14922. PRINT *, x, y
  14923. In a FORTRAN statement, to find the maximum of a list of numbers, only the
  14924. numbers appear:
  14925. PRINT *, MAX(1,3,2)
  14926. The reason programmers can do this in FORTRAN is that statements such as PRINT
  14927. and MAX are part of the language (FORTRAN calls them intrinsic functions). The
  14928. compiler knows their requirements and therefore can supply the appropriate
  14929. information. In C, on the other hand, there is no input, output, or any other
  14930. functionality built into the language except for what the operators provide.
  14931. The C philosophy is to keep the language small and to supply needed
  14932. functionality with libraries. Since the only communication between libraries
  14933. and the compiler is the function call mechanism, you must provide a function
  14934. all the needed information when you call it.
  14935.  
  14936.  
  14937. An Application
  14938.  
  14939.  
  14940. In financial and other numerical applications you often want to express
  14941. integers, such as monetary amounts, as groups of numbers separated by commas:
  14942. $11,235,852
  14943. One approach is to convert the number to a string with sprintf and then
  14944. traverse the string backwards, copying it to another string and inserting
  14945. commas as needed. Another approach, which I present here, solves the more
  14946. general problem of creating strings backwards.
  14947. The program in Listing 9 calls a function prepend to build a string backwards.
  14948. You pass prepend three arguments: the output buffer, an offset which points to
  14949. the first character in the populated portion of the string, and the string to
  14950. prepend. After it's finished, prepend returns the new offset.
  14951. The following diagrams show the state of s[] after each call to prepend:
  14952. Click Here for Diagram
  14953. Listing 10 presents the implementation of prepend along with a function
  14954. preprintf, which allows you to prepend strings with formatting. preprintf uses
  14955. vsprintf to create the formatted string, and then calls prepend to tack it
  14956. onto the front of the existing string.
  14957. I can now implement a function commas, in terms of prepend and preprintf (see
  14958. Listing 11). As I extract each digit in turn, moving right to left by the
  14959. usual remainder and quotient calculations, I push that digit onto a static
  14960. character buffer, inserting commas where necessary. commas returns a pointer
  14961. to the beginning of the completed string, which may or may not coincide with
  14962. the beginning of the buffer. Note that the numeric base and the grouping size
  14963. are parameterized.
  14964.  
  14965.  
  14966. Conclusion
  14967.  
  14968.  
  14969. In this article I have illustrated the hows and whys of variable-length
  14970. argument lists. The stdarg macros are a lot like a parachute -- you don't need
  14971. one very often, but when you do, usually nothing else will suffice. Since
  14972. these macros involve a relaxation of C's argument type-checking mechanism, be
  14973. sure to use them with care.
  14974.  
  14975. Listing 1 Finds the largest of n integers
  14976. /* max1.c */
  14977. #include <stdio.h>
  14978.  
  14979. int maxn(size_t n,...)
  14980. {
  14981. int x;
  14982.  
  14983. int *p = (int *) (&n + 1);
  14984. int m = *p;
  14985.  
  14986. while (--n)
  14987. {
  14988. x = *++p;
  14989. if (x > m)
  14990. m = x;
  14991. }
  14992. return m;
  14993. }
  14994.  
  14995. main()
  14996. {
  14997. printf ("max = %d\n",maxn(3,1,3,2));
  14998. return 0;
  14999. }
  15000.  
  15001. /* Output:
  15002. max = 3
  15003.  
  15004. /* End of File */
  15005.  
  15006.  
  15007. Listing 2 Extracts integers/string argument pairs
  15008. /* vargs1.c */
  15009. #include <stdio.h>
  15010.  
  15011. void int_string_pairs(size_t npairs,...)
  15012. {
  15013. int n;
  15014. char *s;
  15015. char *p = (char *) &npairs + sizeof npairs;
  15016.  
  15017. while (npairs--)
  15018. {
  15019. n = *(int *) p;
  15020. p += sizeof n;
  15021. s = *(char **) p;
  15022. p += sizeof s;
  15023. printf("%d, %s\n",n,s);
  15024. }
  15025. }
  15026. main()
  15027. {
  15028. int_string_pairs(3,1,"one",2,"two",3,"three");
  15029. return 0;
  15030. }
  15031.  
  15032. /* Output:
  15033. 1, one
  15034. 2, two
  15035. 3, three
  15036.  
  15037. /* End of File */
  15038.  
  15039.  
  15040. Listing 3 Encapsulates the parameter extraction logic
  15041. /* vargs2.c */
  15042.  
  15043. #include <stdio.h>
  15044.  
  15045. #define first_arg(x,p) \
  15046. p = (char *) &x + sizeof(x)
  15047. #define next_arg(p,T,x) \
  15048. x = *(T*)p; p += sizeof(T)
  15049.  
  15050. void int_string_pairs(size_t npairs,...)
  15051. {
  15052. int n;
  15053. char *s, *p;
  15054.  
  15055. first_arg(npairs,p);
  15056. while (npairs--)
  15057. {
  15058.  
  15059. next_arg(p,int,n);
  15060. next_arg(p,char *,s);
  15061. printf("%d, %s\n",n,s);
  15062. }
  15063. }
  15064.  
  15065. main()
  15066. {
  15067. int_string_pairs(3,1,"one",2,"two",3,"three");
  15068. return 0;
  15069. }
  15070.  
  15071. /* End of File */
  15072.  
  15073.  
  15074. Listing 4 Uses the macros in stdarg.h to process a variable-lenght argument
  15075. list
  15076.  
  15077. /* vargs3.c */
  15078. #include <stdto.h>
  15079. #include <stdarg.h>
  15080.  
  15081. void int_string_pairs(size_t npairs,...)
  15082. {
  15083. int n;
  15084. char *s;
  15085. va_list args;
  15086.  
  15087. va_start(args,npairs);
  15088. while (npairs--)
  15089. {
  15090. n = va_arg(args,int);
  15091. s = va_arg(args,char *);
  15092. printf("%d, %s\n",n,s);
  15093. }
  15094. va_end(args);
  15095. }
  15096.  
  15097. main()
  15098. {
  15099. int_string_pairs(3,1,"one",2,"two",3,"three");
  15100. return 0;
  15101. }
  15102.  
  15103.  
  15104. /* End of File */
  15105.  
  15106.  
  15107. Listing 5 Implements maxn via the stdarg macros
  15108. /* max2.c */
  15109. #include <stdio.h>
  15110. #include <stdarg.h>
  15111.  
  15112. int maxn(size_t count, ...)
  15113. {
  15114. int n, big;
  15115. va_list numbers;
  15116.  
  15117. va_start(numbers,count);
  15118.  
  15119. big = va_arg(numbers,int);
  15120. while (count--)
  15121. {
  15122. n = va_arg(numbers,int);
  15123. if (n > big)
  15124. big = n;
  15125. }
  15126.  
  15127. va_end(numbers);
  15128. return big;
  15129. }
  15130.  
  15131. main()
  15132. {
  15133. printf("max = %d\n",maxn(3,1,3,2));
  15134. return 0;
  15135. }
  15136.  
  15137. /* End of File */
  15138.  
  15139.  
  15140. Listing 6 Concatenates a variable number of strings
  15141. /* concat.c */
  15142. #include <stdarg.h>
  15143. #include <stdio.h>
  15144. #include <string.h>
  15145.  
  15146. char * concat(char *s,...)
  15147. {
  15148. va_list strings;
  15149. char *p;
  15150.  
  15151. /* Copy first string */
  15152. va_start(strings,s);
  15153. if ((p = va_arg(strings,char *)) == NULL)
  15154. {
  15155. *s = '\0';
  15156. return s;
  15157. }
  15158. else
  15159. strcpy(s,p);
  15160.  
  15161. /* Append others */
  15162. while ((p = va_arg(strings,char *)) != NULL)
  15163.  
  15164. strcat(s,p);
  15165. return s;
  15166. }
  15167.  
  15168. main()
  15169. {
  15170. char buf[BUFSIZ];
  15171. concat(buf,"Sweet","Talker","Betty","Crocker",NULL);
  15172. printf("\"%s\"\n",buf);
  15173. return 0;
  15174. }
  15175.  
  15176. /* Output:
  15177. "SweetTalkerBettyCrocker"
  15178.  
  15179. /* End of File */
  15180.  
  15181.  
  15182. Listing 7 Uses a va_list to populate tables
  15183. #include <stddef.h>
  15184. #include <stdarg.h>
  15185. #include "column.h"
  15186.  
  15187. typedef struct Table
  15188. {
  15189. Column *columns;
  15190. size_t num_columns;
  15191. /* other details omitted */
  15192. } Table;
  15193.  
  15194. void table_put_row(Table *tp, int row, ...)
  15195. {
  15196. if (tp);
  15197. {
  15198. int i;
  15199. va_list strings;
  15200.  
  15201. /* Load each column element from va_list */
  15202. va_start(strings,row);
  15203. for (i = 0; i < tp->num_columns; ++i)
  15204. column_put(tp->columns[i],row, va_arg(strings,char *));
  15205. va_end(strings);
  15206. }
  15207. }
  15208.  
  15209. /* End of File */
  15210.  
  15211.  
  15212. Listing 8 Builds a variable format string by passing a va_list to vfprintf
  15213. /* fatal.c: Exit program with an error message */
  15214.  
  15215. #include <stdio.h>
  15216. #include <stdlib.h>
  15217. #include <stdarg.h>
  15218. #include <string.h>
  15219.  
  15220. void fatal(char *fmt, ...)
  15221. {
  15222. va_list args;
  15223.  
  15224.  
  15225. if (strlen(fmt) > 0)
  15226. {
  15227. va_start(args,fmt);
  15228. vfprintf(stderr,fmt,args);
  15229. va_end(args);
  15230. }
  15231. exit(1);
  15232. }
  15233.  
  15234. /* End of File */
  15235.  
  15236.  
  15237. Listing 9 Illustrates the use of prepend
  15238. #include <stdio.h>
  15239. #include <assert.h>
  15240.  
  15241. #define WIDTH 11
  15242.  
  15243. extern int prepend(char *, unsigned, char *);
  15244.  
  15245. main()
  15246. {
  15247. char s[WIDTH+1];
  15248. int offset = WIDTH;
  15249.  
  15250. s[offset] = '\0';
  15251. offset = prepend(s,offset,"three");
  15252. assert(offset >= 0);
  15253. puts(s+offset);
  15254.  
  15255. offset = prepend(s,offset,"two");
  15256. assert(offset > = 0);
  15257. puts(s+offset);
  15258.  
  15259. offset = prepend(s,offset,"one");
  15260. assert(offset > = 0);
  15261. puts(s+offset);
  15262. return 0;
  15263. }
  15264.  
  15265. /* Output:
  15266. three
  15267. twothree
  15268. onetwothree
  15269.  
  15270. /* End of File */
  15271.  
  15272.  
  15273. Listing 10 Functions to build strings backwards
  15274. /* preprint.c: Functions to prepend strings */
  15275.  
  15276. #include <stdio.h>
  15277. #include <string.h>
  15278. #include <stdarg.h>
  15279. #include <stdlib.h>
  15280.  
  15281. int prepend(char *buf, unsigned offset, char *new_str)
  15282. {
  15283.  
  15284. int new_len = strlen(new_str);
  15285. int new_start = offset - new_len;
  15286. /* Push a string onto the front of another */
  15287. if (new_start >= 0)
  15288. memcpy(buf+new_start,new_str,new_len);
  15289.  
  15290. /* Return new start position (negative if underflowed) */
  15291. return new_start;
  15292. }
  15293.  
  15294. int preprintf(char *buf, unsigned offset, char *format, ...)
  15295. {
  15296. int pos = offset;
  15297. char *temp = malloc(BUFSIZ);
  15298.  
  15299. /* Format, then push */
  15300. if (temp)
  15301. {
  15302. va_list args;
  15303.  
  15304. va_start(args,format);
  15305. vsprintf(temp,format,args);
  15306. pos = prepend(buf,offset,temp);
  15307. va_end(args);
  15308. free(temp);
  15309. }
  15310. return pos;
  15311. }
  15312.  
  15313. /* End of File */
  15314.  
  15315.  
  15316. Listing 11 Uses prepend and preprintf to format numbers with comma separators
  15317. /* commas.c: Converts a number into a string with commas */
  15318.  
  15319. #include <stdio.h>
  15320.  
  15321. #define BASE 10
  15322. #define GROUP 3
  15323.  
  15324. /* Need space to hold the digits of an unsigned long,
  15325. * intervening commas and a null byte. It depends on
  15326. * BASE and GROUP above (but logarithmically, not
  15327. * as a constant. so we must define it manually here)
  15328. */
  15329. #define MAXTEXT 14 /* For BASE = 10 */
  15330.  
  15331. int prepend(char *, unsigned, char *);
  15332. int preprintf(char *, unsigned, char *, ...);
  15333.  
  15334. char *commas(unsigned long amount)
  15335. {
  15336. short offset = MAXTEXT-1, /* where the string "starts" */
  15337. place; /* the power of BASE for */
  15338. /* current digit */
  15339. static char text[MAXTEXT];
  15340.  
  15341. text[offset] = '\0';
  15342.  
  15343.  
  15344. /* Push digits right-to-left with commas */
  15345. for (place = 0; amount > 0; ++place)
  15346. {
  15347. if (place % GROUP == 0 && place > 0)
  15348. offset = prepend(text,offset,",");
  15349. offset = preprintf(text,offset,"%x",amount % BASE);
  15350. amount /= BASE;
  15351. }
  15352. return (offset >= 0) ? text + offset : NULL;
  15353. }
  15354.  
  15355. main()
  15356. {
  15357. puts(commas(1));
  15358. puts(commas(12));
  15359. puts(commas(123));
  15360. puts(commas(1234));
  15361. puts(commas(12345));
  15362. puts(commas(123456));
  15363. puts(commas(1234567));
  15364. puts(commas(12345678));
  15365. puts(commas(123456789));
  15366. puts(commas(1234567890));
  15367. return 0;
  15368. }
  15369.  
  15370. /* Output:
  15371. 1
  15372. 12
  15373. 123
  15374. 1,234
  15375. 12,345
  15376. 123,456
  15377. 1,234,567
  15378. 12,345.678
  15379. 123,456,789
  15380. 1,234,567,890
  15381. */
  15382.  
  15383. /* End of File */
  15384.  
  15385.  
  15386.  
  15387.  
  15388.  
  15389.  
  15390.  
  15391.  
  15392.  
  15393.  
  15394.  
  15395.  
  15396.  
  15397.  
  15398.  
  15399.  
  15400.  
  15401.  
  15402.  
  15403.  
  15404.  
  15405.  
  15406.  
  15407. CUG New Releases
  15408.  
  15409.  
  15410. C++SIM, NNUTILS, and a Small-but-Good Combo
  15411.  
  15412.  
  15413.  
  15414.  
  15415. Victor R. Volkman
  15416.  
  15417.  
  15418. Victor R. Volkman received a BS in Computer Science from Michigan
  15419. Technological University. He has been a frequent contributor to The C Users
  15420. Journal since 1987. He is currently employed as Senior Analyst at H.C.I.A. of
  15421. Ann Arbor, Michigan. He can be reached by dial-in at the HAL 9000 BBS (313)
  15422. 663-4173 or by Usenet mail to sysop@hal9k.com.
  15423.  
  15424.  
  15425.  
  15426.  
  15427. Introduction
  15428.  
  15429.  
  15430. Every month I get dozens of e-mail inquiries about how to obtain archive
  15431. distributions listed in these monthly columns. I only reply that people should
  15432. use the C Users Order Form in the center portion of the magazine. Using the
  15433. Order Form is simpler and easier than chasing after archives from online
  15434. services.
  15435. Of course, the CUG does not claim to be the exclusive distributor of anything
  15436. in its library. This means that these archives are frequently available from
  15437. many online sources, including FTP servers, Compuserve, and dial-up BBSes.
  15438. These online sources often have older versions or sometimes just patches. What
  15439. CUG guarantees is that each archive is completely intact, and fully authorized
  15440. for distribution.
  15441.  
  15442.  
  15443. New Library Acquisitions
  15444.  
  15445.  
  15446. C++SIM (CUG#394): Simula-style class libraries for discrete event process
  15447. based simulation.
  15448. Input-Edit, SORTLIST AVL, and Typing Tutor (CUG #395): multi-platform
  15449. user-input line editor, AVL balanced binary tree, and typing instructor for
  15450. Curses
  15451. NNUTILS (CUG #396): neural net source library & tutorial
  15452.  
  15453.  
  15454. C++SIM Discrete Simulations: CUG 394
  15455.  
  15456.  
  15457. C++SIM is a newly released package from M.C. Little, at the Department of
  15458. Computing Science in the University of Newcastle upon Tyne (England), and D.L.
  15459. McCue at Xerox Corp. (Webster, NY). The C++SIM discrete event process based
  15460. simulation package provides Simula-style class libraries. The same
  15461. distribution also includes the SIMSET linked list manipulation facilities.
  15462. (According to MacLennan [1], Simula was the first computer language to
  15463. incorporate the ideas of "class" and "object" constructs back in 1967.) SIM++
  15464. currently claims usability only on UNIX workstations, such as SUN Spares.
  15465. C++SIM version 1.0 (released 06/15/92) is now available as CUG volume #394.
  15466. C++SIM uses inheritance throughout the design to an even greater extent than
  15467. already provided by Simula. C++SIM's use of inheritance allows you to add new
  15468. functionality without affecting the overall system structure. Thus, C++SIM
  15469. provides for a more flexible and expandable simulation package.
  15470. C++SIM provides the following classes: Process, ProcessList, ProcessIterator,
  15471. ProcessCons, Random, Element & Head, thread, lwp_thread, and gnu_thread.
  15472. C++SIM includes a 20-page paper entitled "Construction and Use of a Simulation
  15473. Package in C++." This paper is available in PostScript format only. The paper
  15474. describes the class hierarchy itself as well as how to further refine the
  15475. simulation package.
  15476. The simulation package requires a threads package and currently works only
  15477. with the Sun lightweight process library or the included GNU thread package.
  15478. The thread library is the only system-specific code, so porting the remainder
  15479. of the code to other UNIX workstations should be easy. C++SIM compiles with
  15480. Cfront 2.1 and Cfront 3.0.1 and GNU g++ 2.3.3
  15481. The C++SIM license grants permission to use, copy, modify, and distribute the
  15482. program for evaluation, teaching and/or research purposes only and without
  15483. fee. The University of Newcastle upon Tyne copyright and permission notice
  15484. must appear on all copies and supporting documentation, and similar conditions
  15485. are imposed on any individual or organization to whom the program is
  15486. distributed.
  15487.  
  15488.  
  15489. Input-Edit, SORTLIST AVL, and Typing Tutor: CUG #395
  15490.  
  15491.  
  15492. This volume combines three relatively small but powerful archives on a single
  15493. diskette. Chris Thewalt (University of California at Berkeley, Civil
  15494. Engineering) presents his interactive line editor library. Walter Karas (Cary,
  15495. NC) contributes his implementation of the classic binary search tree with AVL
  15496. balancing. Last, Christopher Sawtell (Linwood, Christchurch, New Zealand)
  15497. releases his Typing Tutor for use with Curses. All three are immediately
  15498. available as CUG volume #395.
  15499.  
  15500.  
  15501. Input-Edit: CUG #395A
  15502.  
  15503.  
  15504. Input-Edit, also known as getline, greatly increases the functionality of
  15505. programs which read input a line at a time. With Input-Edit, interactive
  15506. programs that read input line by line can now provide line editing and a
  15507. history buffer to the end user that runs the program. As far as the programmer
  15508. is concerned, the program only asks for the next line of input. However, until
  15509. the user presses the RETURN key he can use Emacs-style line editing commands
  15510. and can traverse the history buffer of lines previously typed.
  15511. Other packages, such as GNU's readline, have greater capability but are also
  15512. substantially larger. Input-edit is small (1200 source lines) and quite
  15513. portable because it uses neither stdio nor any termcap features. For example,
  15514. Input-edit only uses \b to backspace and \007 to ring the bell on errors.
  15515. Since Input-edit cannot edit multiple lines, it scrolls long lines left and
  15516. right on the same line.
  15517. Input edit is written in K&R C and can run on any UNIX system (BSD, SYSV or
  15518. POSIX), AIX, and XENIX, as well as non-UNIX systems such as MS-DOS with MSC,
  15519. Borland Turbo C, or djgpp, OS/2 with gcc (EMX), and DEC VAX-11 VMS. Porting
  15520. Input-Edit to new systems consists mainly of altering the package's character
  15521. read function to read a character when it is typed without echo.
  15522.  
  15523.  
  15524. Sortlist AVL: CUG #395B
  15525.  
  15526.  
  15527.  
  15528. SORTLIST implements a "sorted list" data structure library in ANSI C. This
  15529. library is appropriate whenever all elements of the sorted list have the
  15530. following characteristics:
  15531. 1. All elements are of a single fixed size.
  15532. 2. Each element is associated with a unique key value.
  15533. 3. The set of key values has a well-defined "less than, greater than"
  15534. relation.
  15535. Symbol tables and dictionary applications are excellent candidates for the
  15536. sorted list data structure. This implementation of a sorted list data
  15537. structure employs an AVL tree. AVL trees were invented by Adelson-Velskii and
  15538. Landis in 1962. Specifically, Karas draws on algorithms presented by Horowitz
  15539. and Sahni in Fundamentals of Data Structures (Computer Science Press). The
  15540. add, find, and delete operations on an AVL tree have worst-case O (n) time
  15541. complexity. SORTLIST version 1.1 (released 8/25/93) is now available on CUG
  15542. volume #395.
  15543.  
  15544.  
  15545. Typing Tutor: CUG #395C
  15546.  
  15547.  
  15548. The Typing Tutor for use with Curses is a marvel of compactness. Since it
  15549. builds on the substantial functionality of the UNIX Curses library, the Typing
  15550. Tutor consists of just 250 source lines. Typing Tutor's learning scenario is
  15551. simple, yet easily customizable to fit any lesson plan. Typing Tutor's screen
  15552. displays two windows. The top window contains the "lesson" which is the text
  15553. you will be typing from. The bottom window contains the results of your
  15554. (presumably) touch typing. Every time a character in the bottom window fails
  15555. to match the original in the top window, Typing Tutor flags it by changing the
  15556. screen attribute to flashing. Although Sawtell does not specify compatability
  15557. for Typing Tutor, he expects it to run on any UNIX system with a Curses
  15558. package available.
  15559.  
  15560.  
  15561. NNUTILS Neural Network: CUG #396
  15562.  
  15563.  
  15564. NNUTILS, by Gregory Stevens (Rochester, NY), is a public domain package to
  15565. help you to start programming neural networks in C. NNUTILS is a tutorial with
  15566. source code as your textbook. Stevens' intensely documented source code
  15567. contains everything you need to implement several kinds of net architectures.
  15568. NNUTILS gives you a series of simple implementations to let you see how they
  15569. work step by step.
  15570. Each NNUTILS subdirectory contains a different example application with six
  15571. standard C source files and a main program. The source is written in
  15572. ANSI-compliant C and developed primarily under Borland C++ 3.1. Accordingly,
  15573. the CUG distribution includes DOS executables and project files for each
  15574. implementation. Because the code is ANSI compliant, all of the examples work
  15575. with GNU C under UNIX. Building executables with GNU C is simple enough
  15576. without makefiles, so none are included.
  15577. Briefly, here's a summary of problem sets included with NNUTILS:
  15578. NNTEST1: A network with one input node and one output node.
  15579. NNTEST2: A network using the "logistic" activation function (as opposed to a
  15580. linear function).
  15581. NNXOR: A network implementation of the famous Exclusive-Or problem.
  15582. NNSIM1: A generic feed-forward network with back propagation.
  15583. NNWHERE: A 5x5 retina is presented with shapes it must classify correctly.
  15584. NNWHAT: A continuation of above where shapes can assume any position.
  15585. NNEVOLVE: A feed-forward, back-propagation supervised net.
  15586. NNSIM2: Simulates a competitive learning network with a Hebbian learning
  15587. algorithm.
  15588. NNRECUR1: A recurrent net with back propagation, and a kind of supervised
  15589. learning called Self-Correlating, where the input patterns serve as their own
  15590. output patterns.
  15591. NNUTILS versions 1.01 (released 08/02/93) is immediately available as CUG
  15592. #396.
  15593. References
  15594. [1] MacLennan, Bruce J., Principles of Programming Languages: Design,
  15595. Evaluation, and Implementation. New York: H.H. Rhinehart, and Winston. 1983.
  15596.  
  15597.  
  15598.  
  15599.  
  15600.  
  15601.  
  15602.  
  15603.  
  15604.  
  15605.  
  15606.  
  15607.  
  15608.  
  15609.  
  15610.  
  15611.  
  15612.  
  15613.  
  15614.  
  15615.  
  15616.  
  15617.  
  15618.  
  15619.  
  15620.  
  15621.  
  15622.  
  15623.  
  15624.  
  15625.  
  15626. Editor's Forum
  15627. The C community is waking up once again. For years, C has been the stable
  15628. alternative, the language of choice where performance, precision, and
  15629. portability count. Want thrills and excitement? Go try your hand at C++.
  15630. That's where most of the clever new ideas get tried out these days. C++ has
  15631. reveled in an adolescent vigor because it could depend on C to provide a
  15632. stable springboard.
  15633. Now the shoe is on the other foot, at least to some extent. Work on revising
  15634. the C Standard is commencing two years earlier than required by ISO rules. ISO
  15635. committee WG14 and ANSI X3J11 both unanimously agreed at their December
  15636. meeting in Kona, Hawaii on an outline for bringing Standard C more up to date.
  15637. Even more important, the joint committee agreed on a number of guiding
  15638. principles for introducing changes to the language. (You'll be reading more
  15639. about those principles in these pages in the months and years to come.)
  15640. Closing the gap with C++ ranked very near the top of that list of principles.
  15641. A strength of C++, sometimes forgotten, is that it remains largely upward
  15642. compatible with C. You can always fall back on the older language and still
  15643. write the "interesting" parts in C++. A weakness of C++, sometimes
  15644. underestimated, is that it thus feels unconstrained. The sentiment among those
  15645. standardizing C++ is that it should handle all coding challenges from the
  15646. world of object-oriented programming, however much they complexify the
  15647. language.
  15648. We self-appointed custodians of Standard C appreciate the real contributions
  15649. made by C++ to the programmer's toolchest. At the same time, we fear losing
  15650. "the spirit of C" in our zeal to keep up with the times. Our safety valve is
  15651. to apply the C++ trick in reverse. We're eager to add classes to C, but
  15652. probably not templates -- polymorphism, but maybe not multiple inheritance.
  15653. Sure there are times when you need those more advanced features. Maybe those
  15654. are the times when you should mix in a little C++ code. The rest of the time,
  15655. let's keep C efficient and conceptually simple.
  15656. Bjarne Stroustrup, the developer of C++, has privately expressed concern to me
  15657. that this new C standardization activity could lead to dueling standards. He
  15658. rightly fears a civil war between the two most popular programming languages.
  15659. I certainly don't want to see anything of the sort come to pass. So long as we
  15660. focus on keeping a large common subset between the two languages, I don't
  15661. think that it will. For years, the watchword of the C++ standards effort has
  15662. been, "as close as possible to C, but no closer." Now the watchword of the C
  15663. standards effort should be, "closer to C++ than you thought possible, but not
  15664. too close."
  15665. P.J. Plauger
  15666. pjp@plauger. com
  15667.  
  15668.  
  15669.  
  15670.  
  15671.  
  15672.  
  15673.  
  15674.  
  15675.  
  15676.  
  15677.  
  15678.  
  15679.  
  15680.  
  15681.  
  15682.  
  15683.  
  15684.  
  15685.  
  15686.  
  15687.  
  15688.  
  15689.  
  15690.  
  15691.  
  15692.  
  15693.  
  15694.  
  15695.  
  15696.  
  15697.  
  15698.  
  15699.  
  15700.  
  15701.  
  15702.  
  15703.  
  15704.  
  15705.  
  15706.  
  15707.  
  15708.  
  15709.  
  15710.  
  15711.  
  15712.  
  15713.  
  15714.  
  15715.  
  15716.  
  15717.  
  15718.  
  15719.  
  15720. New Products
  15721.  
  15722.  
  15723. Industry-Related News & Announcements
  15724.  
  15725.  
  15726.  
  15727.  
  15728. Liant Ships C++/Views v3.0 Visual Programming Tool
  15729.  
  15730.  
  15731. Liant Software has begun shipping their C++/Views v3.0 Visual Programming
  15732. Tool. C++/Views v3.0 is an object-oriented development tool that provides
  15733. support for creating and porting GUI applications among Microsoft Windows,
  15734. OS/2 Presentation Manager, OSF/Motif, Apple Macintosh, and DOS character mode
  15735. environments. C++/Views v3.0 combines 100 ready-to-use classes with programmer
  15736. productivity tools. C++/Views v3.0 includes interface, data, event, printer,
  15737. and extended GUI classes. With C++/Views, programmers can create native GUI
  15738. applications because C++/Views uses the local GUIs toolkit.
  15739. C++/Views v3.0 includes a visual development tool, called C++/Views
  15740. Constructor, which lets developers work visually with the C++/Views class
  15741. library. Constructor unites a visual interface builder with an enhanced
  15742. C++/Views Browser, letting users switch between drawing and archiving their
  15743. portable resources to editing code which calls these resources.
  15744. The C++/Views Interface Builder is a WYSIWYG editing tool for designing and
  15745. testing the behavior of portable resources such as binary files of bitmaps,
  15746. dialogs, and menus, or other GUI objects. Portable resources are called from
  15747. an application at run-time. The same resource file can be called from a
  15748. Windows, Motif, Presentation Manager, Macintosh, or DOS application, and will
  15749. have a "look and feel" consistent with the host environment under which it
  15750. runs.
  15751. Other features of C++/Views v3.0 include geometry management and C++/Views
  15752. Browser v3.0. The Browser is an MDI application which lets users cut-and-paste
  15753. among multiple C++ applications. C++/Views v3.0 ranges from $499 to $1,999
  15754. depending on platform. There are no royalties or run-time fees. Upgrades range
  15755. from $149 to $279 depending on platform. For more information contact Liant
  15756. Software Corporation, 959 Concord St., Framingham, MA 01701, (508) 872-8700;
  15757. FAX: (508) 626-2221.
  15758.  
  15759.  
  15760. Borland Announces ObjectWindows for Novell's AppWare
  15761.  
  15762.  
  15763. Borland International Inc. has announced that it will combine its
  15764. ObjectWindows Library (OWL) with Novell's AppWare Foundation technology to
  15765. provide developers with C++-based, cross-platform development libraries.
  15766. Borland's ObjectWindows is a set of class libraries for developing
  15767. applications in C++ that provides a set of pre-made objects for developing
  15768. Windows Applications. The Novell AppWare Foundation is a set of C libraries
  15769. that provide developers cross-platform functionality across multiple operating
  15770. systems, graphical interfaces and network services. ObjectWindows for AppWare
  15771. will let developers of applications for Microsoft Windows move their
  15772. applications to Apple's Macintosh, IBM's OS/2, and UNIX platforms.
  15773. ObjectWindows for AppWare is based on ObjectWindows v2.0. ObjectWindows 2.0 is
  15774. a Microsoft Windows specific implementation of the ObjectWindows application
  15775. framework. ObjectWindows for AppWare replaces the Microsoft Windows specific
  15776. implementation with the AppWare Foundation cross platform API. The result lets
  15777. the application run on Microsoft Windows, Macintosh, UNIXWare, SunOS, and
  15778. HP-UX.
  15779. An Early Experience Program is planned for the first quarter of 1994, and both
  15780. companies plan to begin distributing ObjectWindows for AppWare in the summer
  15781. of 1994. For more information contact Borland International Inc., 100 Borland
  15782. Way, P.O. Box 660001, Scott's Valley, CA 95067, (408) 431-5172.
  15783.  
  15784.  
  15785. The Haley Enterprise Ships Rete++
  15786.  
  15787.  
  15788. The Haley Enterprise has begun shipping Rete++, a new product that integrates
  15789. rule-based programming with C++. Rete++ supports rule-based expert system
  15790. development by providing rules that match against C++ objects using the Rete
  15791. Algorithm.
  15792. Rete++ provides both forward and backward chaining, and includes a
  15793. comprehensive class hierarchy for convenient development of rule-based C++
  15794. applications. Rete++ generates C++ class taxonomies for use in C++
  15795. applications, creating C++ header files with class definitions. Rete++ also
  15796. generates a class for each type of object that an application references in
  15797. any rule. C++ applications can use the Rete++-generated classes directly or
  15798. can further subclass them as needed.
  15799. With Rete++, an application can assert facts either via the rule-based
  15800. deffacts statement or directly from C/C++, C++ operator overloading allows
  15801. elements to be expressed using standard C++ syntax, eliminates coding of
  15802. complex, type-specific conditional logic, and reduces the need for
  15803. operation-specific function names. Rather than invent a strongly-typed
  15804. C++-like syntax for rules, Rete++ retains the traditional parenthesis
  15805. delimited OPS5/ART/CLIPS/Eclipse syntax.
  15806. Rete++ supports Microsoft Windows, Microsoft Windows NT, SUN Solaris,
  15807. NeXTStep, Apple's Macintosh, and HP-UX. For more information contact The Haley
  15808. Enterprise, Inc., 413 Orchard St., Sewickley, PA 15143,
  15809. (412)967-1100;FAX:(412)741-6457.
  15810.  
  15811.  
  15812. Applied Microsystems Announces X-Windows GUI for Real-Time Debugger
  15813.  
  15814.  
  15815. Applied Microsystems has announced debugging support for Microtec Research
  15816. Inc.'s XRAY MasterWorks. The debugger, MWX-ICE (Multi Windows XRAY for In
  15817. Circuit Emulation), is based on MRI's multi-windows XRAY debugger and includes
  15818. in-circuit emulator additions. MWX-ICE can be used as a stand-alone product or
  15819. connected to the XRAY MasterWorks environment.
  15820. MWX-ICE debugger gives the user control over execution of C, C++ and assembly
  15821. language programs. Separate windows for execution trace history, registers,
  15822. and stacks lets the user browse, and make modifications. MWX-ICE lets the user
  15823. display and change variables, including structures and unions. User-defined
  15824. macros can automatically be called when a breakpoint is encountered. Control
  15825. of the emulator's overlay capability, breakpoint, and event system is
  15826. provided. Supported load formats include IEEE 695, COFF, and A.OUT. Other
  15827. features of MWX-ICE include: a Motif-based user interface which combines
  15828. debugger controls with function notebooks; configurable control panel buttons
  15829. that activate various debugging commands; and an on-line, context-sensitive
  15830. hypertext help system.
  15831. MWX-ICE supports Motorola familys of 68000, 68EC/HC000, 68302,
  15832. 68020/030/EC030, 68330/340, 68331/332, 68360/EN360, and Intel's 80960CA/CF
  15833. processors. MWX-ICE is $3000. For more information contact Applied
  15834. Microsystems Corporation, 5020 148th Ave. N.E., P.O. Box 97002, Redmond, WA
  15835. 98073, (206) 882-2000 or (800) 426-3925; FAX: (206) 883-3049; Telex: 185196.
  15836.  
  15837.  
  15838. Optimite Systems Ships PC_Opt
  15839.  
  15840.  
  15841. Optimite Systems has begun shipping PC_Opt, a post compile optimizer. PC_Opt
  15842. analyzes across object modules and run-time libraries and performs
  15843. optimizations on object code. The optimizations performed by PC_Opt are in
  15844. addition to traditional compiler-based optimizations. These additional
  15845. optimizations are made possible by analyzing the complete object code, as
  15846. opposed to isolated modules in compiler-based optimization.
  15847. Features of PC_Opt include: converting far call and return instructions to
  15848. near call and return instructions, converting stack clearing code from C to
  15849. Pascal format, and registering parameter passing. PC_Opt also deletes
  15850. unreachable code. PC_Opt requires no source code modification or setup. PC_Opt
  15851. lets users specify the set of input object module files that would normally be
  15852. specified during the link process. PC_Opt output is in ready-to-link OMF
  15853. standard object module format.
  15854. PC_Opt supports object modules and run-time libraries generated by Borland C++
  15855. v3.1 (C source only), and other standard OMF format object modules. PC_Opt is
  15856. $15. For more information contact Optimite Systems, 1000 Singleton Blvd.,
  15857. Dallas, TX 75212, (214) 745-1301; FAX: (214) 747-3614.
  15858.  
  15859.  
  15860. Gimpel Releases PC-lint for C/C++
  15861.  
  15862.  
  15863. Gimpel Software has released PClint for C/C++. PC-lint for C/C++ will analyze
  15864. a mixed suite of C/C++ programs and report on bugs, glitches, and
  15865. inconsistencies. PC-lint provides a number of C++-specific checks that include
  15866. reminders to virtualize inherited destructors, and to create custom assignment
  15867. operators and copy constructors for classes. Other checks include new and
  15868. delete imbalances, name hiding, and unusual but legal constructs. In addition,
  15869. PC-lint for C/C++ provides the same checks for C as PC-lint for C. These
  15870. checks include strong type checking, a control-flow based analysis of variable
  15871. initialization, loss of precision, strange uses of Booleans, unaccessed
  15872. variables, unusual macros, and unused program components.
  15873. PC-lint for C/C++ is compatible with Borland C/C++ and Microsoft C/C++ and
  15874. their respective Application Frameworks. PC-lint for C/C++ is based on the
  15875. Annotated C++ Reference Manual (ARM), and is tracking the ANSI/ISO X3J16
  15876. standardization process, including templates and exceptions. PC-lint for C/C++
  15877. provides both a DOS-OS/2 bound executable and a 386 DOS-extended executable.
  15878. The cost to license PC-lint for C/C++ is $239. For more information contact
  15879. Gimpel Software, 3207 Hogarth Lane, Collegeville, PA 19426, (215) 584-4261.
  15880.  
  15881.  
  15882.  
  15883. Spectrum Systems Releases ObjectBase
  15884.  
  15885.  
  15886. Spectrum Systems, Inc. has released ObjectBase, an object-oriented C++ class
  15887. library for relational databases. When using ObjectBase, tables or groups of
  15888. tables may be accessed as objects. Data redundancy is hidden from the user.
  15889. According to the company, the programming task of interfacing to the DBMS is
  15890. simplified, without taking away the programmer's ability to access the
  15891. database directly through traditional routes, e. g. SQL.
  15892. Features of ObjectBase include: transparent shared connections and shared
  15893. connection groups; transparent multiple connections to multiple servers; data
  15894. retrieval via SQL and stored procedures; blocking, non blocking, and
  15895. asynchronous data retrieval; program independence from details of column
  15896. definition; transactions; and customizable error handling. Objectbase can
  15897. create an error handler that will attempt to re-establish lost connections
  15898. between client and server, without intervention by the user. ObjectBase can
  15899. also automatically generate both the class description (the header file) and
  15900. the class implementation (the source code) for tables from the schema
  15901. description.
  15902. ObjectBase supports Sybase SQL Server and Microsoft SQL Server. ObjectBase
  15903. developer/run-time licenses range from $899 to $2,699. For more information
  15904. contact Spectrum Systems, Inc., Woodfield Corporate Center, 425 N. Martingale
  15905. Rd, Suite 800, Schaumburg, IL 60173, (708) 330-3797 or (708) 706-3800; FAX:
  15906. (708) 706-3700.
  15907.  
  15908.  
  15909. Rogue Wave Upgrades Tools.h++
  15910.  
  15911.  
  15912. Rogue Wave Software, Inc. has upgraded their C++ class library, Tools.h++.
  15913. Tools.h++ v6.0 includes "Internationalized" support which provides the ability
  15914. to work with world-wide, local character sets. With internationalization,
  15915. programmers can write a single application that can be shipped to many
  15916. countries. This application, when executed, will be able to process times,
  15917. dates, strings, and currency in the native format.
  15918. Features of Tools.h++ v6.0 include: multi-byte and wide character strings;
  15919. classes that parse and format times, dates, and currency in multiple locales;
  15920. and classes that support multiple time zones, daylight savings rules, and
  15921. localize I/O streams and messages. Other features of Tools.h++ v6.0 include
  15922. supporting exceptions as specified by the emerging draft of the ANSI C++
  15923. standard, support for multiple-threaded programs, and an RWCString class.
  15924. Tools.h++ v6.0 is available on Windows NT, Macintosh, DOS, Windows 3.x, OS/2,
  15925. and UNIX. Tools.h++ v6.0 ranges from $299 to $395 depending on platform.
  15926. Upgrades are available to current users of the Tools.h++ library at $99 for
  15927. DOS, and $135 for Windows 3.x, OS/2, and UNIX. In addition to Tools.h++ v6.0,
  15928. Rogue Wave will be providing ToolsPro.h++. Rogue Wave ToolsPro.h++ contains
  15929. the implementation of the Tools.h++ library, plus test suites for the
  15930. Tools.h++ library. For more information contact Rogue Wave Software, Inc.,
  15931. P.O. Box 2328, Corvallis, OR 97339, (503) 754-3010; FAX: (503) 757-6650.
  15932.  
  15933.  
  15934. Computer Innovations Releases C++ Language v3.0 for UnixWare
  15935.  
  15936.  
  15937. Computer Innovations, Inc. has released C++ Language v3.0 for UnixWare, a
  15938. USL's industry standard language, and combines both super-C functionality and
  15939. object-oriented programming. In addition to supporting templates as part of
  15940. the compiler, C++ Language Releases v3.0 for UnixWare produces object code
  15941. that is optimized for the Intel 486/Pentium platform.
  15942. Standard UNIX utilities are used to install C++ Language v3.0 for UnixWare,
  15943. and all necessary header files are built specifically for the UnixWare system.
  15944. C++ Language v3.0 for UnixWare is $349. For more information contact Computer
  15945. Innovations Inc., 1129 Broad St., Shrewsbury, NJ07702, (908) 542-5920; FAX:
  15946. (908) 542-6121.
  15947.  
  15948.  
  15949. Alsys Announces Object CM
  15950.  
  15951.  
  15952. The Alsys CASE Division has announced Object CM, an object-oriented
  15953. configuration management system. Object CM integrates an object base which is
  15954. compliant with Portable Common Tools Environment (PCTE).
  15955. Features of Object CM include a PCTE-based object-oriented repository, a
  15956. graphical use interface, an object browser, a system administration facility,
  15957. and a variety of productivity tools. Integral to Object CM is a single control
  15958. facility, which provides for the creation and tracking of trouble reports and
  15959. modification requests, and their correct association with the software
  15960. objects.
  15961. ObjectCM can be extended to support process control over activities such as
  15962. software design, coding, documentation, project management, and software
  15963. reuse. Object CM interfaces with third-party CASE and documentation tools, and
  15964. overlays a control structure which manages their use throughout the project.
  15965. Object CM is the core module in the FreedomWorks family of CASE integration
  15966. products. Object CM is licensed for $2,500 to $3,500 per seat, depending on
  15967. quantity. For more information contact Alsys CASE Division, 10251 Vista
  15968. Sorrento Pkwy, Suite 300, San Diego, CA 92121, (619) 457-2700; FAX: (619)
  15969. 452-2117.
  15970.  
  15971.  
  15972. DC Micro Development Ships Crusher! Data Compression Toolkit
  15973.  
  15974.  
  15975. DC Micro Development has begun shipping the Crusher! Data Compression Toolkit.
  15976. Crusher! is designed for use with C and is compatible with both DOS and UNIX
  15977. systems. Crusher! provides portable source code, and produces multi-file
  15978. archives compatible with both DOS and UNIX. Crusher! supports Borland C/C++
  15979. v3.1 and Microsoft/C++ v7.1 compilers for DOS, and ANSI or K&R C for UNIX
  15980. System V.
  15981. Features of Crusher! include: layered design; buffer allocation; compression
  15982. of ASCII and binary data; 32-bit CRC file integrity checking; multiple-file
  15983. archive support; UNIX-style wildcard support for DOS applications; full
  15984. support for subdirectories; user-definable callback functions; ARQ source
  15985. code; and portability to other architectures. According to the company,
  15986. "Typical compression ratios (when using Crusher!) are 50%, while many
  15987. database, spreadsheet, and ASCII files compress to 20% or less of their
  15988. original size."
  15989. Crusher! Data Compression Toolkit is $239.90 including source code. An
  15990. evaluation version with full documentation is available for $35. For more
  15991. information contact DC Micro Development, 3554 Creekwood Dr. #7, Lexington, KY
  15992. 40502, (606) 268-1559; FAX: (606) 266-0726; BBS: (606)268-1251.
  15993.  
  15994.  
  15995. FSI Announces HyperC Compiler
  15996.  
  15997.  
  15998. Fortunel Systems, Inc. (FSI) has announced a compiler for a new, portable
  15999. data-parallel language, HyperC. HyperC was built as an extension to C that
  16000. allows programmers to express the parallelism of an application, and run the
  16001. application on (massively) parallel systems. HyperC was designed with the
  16002. goals of efficient expression and compilation of parallel programs across
  16003. different architectures. Constructs in the language emphasize: increasing
  16004. local computation, minimizing communication requirements, and overlapping
  16005. communications with computations.
  16006. Data parallelism is achieved by applying the same operation across all the
  16007. elements of a data set. HyperC provides "collections" as data sets for data
  16008. parallel operations. Given overloading of operations and functions, a compact
  16009. syntax can express data parallel computations. As an extension to C, HyperC
  16010. supports incremental parallelization of existing C programs.
  16011. HyperC is available for workstations including Sun, SGI, HP, and DEC. A PVM
  16012. version is being developed and is planned for release by the end of March,
  16013. 1994. For more information contact Fortunel Systems, Inc., 1135 Kildaire Farm
  16014. Rd., Ste. 311-5, Cary, NC 27511, (919) 319-1624; FAX: (919) 319-1749; e-mail:
  16015. fortunel@vnet.net.
  16016.  
  16017.  
  16018. Greenleaf Introduces Archivelib
  16019.  
  16020.  
  16021. Greenleaf Software, Inc. has introduced ArchiveLib, a Windows compatible data
  16022. compression and archive library for C/C++ programmers. ArchiveLib is an
  16023. object-oriented data-compression run-time library, with equivalent C functions
  16024. for C developers. Using a two-stage process, consisting of two different
  16025. data-compression methods, ArchiveLib compresses ASCII or binary data into an
  16026. archive for storage.
  16027. The series of 100 ArchiveLib functions maintain language independence and
  16028. under Windows, ArchiveLib is also available as language independent DLL. With
  16029. ArchiveLib, programmers can compress and archive buffers of data within their
  16030. applications without having to store them as a file. Compressed data can be
  16031. retrieved into either a disk file or a memory buffer. Users can also create
  16032. their own data objects. The archiving features found in ArchiveLib can serve
  16033. several functions: transmitting or storing files via a medium that does not
  16034. support a file system; moving files from one operating system to another;
  16035. distributing software to customers; and storing internal program data.
  16036. ArchiveLib is $279 and includes full source code (excluding the proprietary
  16037. data compression algorithm) and a technical support package. For more
  16038. information contact Greenleaf Software, Inc., 16479 Dallas Pkwy. Ste 570,
  16039. Dallas, TX 75248, (214) 248-2561; FAX: (214) 248-7830.
  16040.  
  16041.  
  16042. Century Computing Releases TAE Plus v5.3
  16043.  
  16044.  
  16045.  
  16046. Century Computing has released TAE Plus v5.3. TALE Plus v5.3 is a portable
  16047. software development environment that supplies both development tools for
  16048. creating GUIs and management tools for controlling their application's user
  16049. interface at run-time.
  16050. Features of TAE Plus v5.3 include: the TAE Plus Workbench which lets the user
  16051. lay out an interface interactively; Code Generator and Code Merge which
  16052. generate the code in C, (ANSI or K&R), C++, or ADA; Dynamic Data Objects which
  16053. let the designer create GUIs that use dials, gauges, pictures, maps, switches,
  16054. icons, or animation to communicate with users; prototyping which lets the
  16055. developer generate prototype interfaces, and iteratively test and refine them;
  16056. and automated scripting which provides the capability for automated testing,
  16057. on-line demos, tutorials, and useability testing.
  16058. TAE Plus v5.3 supports Sun OS (both Motif v1.1.4 and v1.2). TAE Plus v5.3
  16059. licensing agreements are based upon the number of TAE developers in a
  16060. workgroup. Fees range from $2,250 to $11,200, with discounts for workgroups of
  16061. more that 15 developers. For more information contact Century Computing Inc.,
  16062. 1014 West Street, Laurel, MD 20707, (301) 953-3330 or (880) 823-3228.
  16063.  
  16064.  
  16065. Odyssey Ships ISYS Developers' Toolkit
  16066.  
  16067.  
  16068. Odyssey Development, Inc. has begun shipping the ISYS Developer's Toolkit, a
  16069. search engine for developers and OEMs. With the toolkit, users can integrate
  16070. the ISYS text retrieval engine with applications such as CD-ROM authoring,
  16071. electronic publishing, document preparation, and image management.
  16072. ISYS Developer's Toolkit provides access to text information residing in word
  16073. processor and other files. Users key in a word or phrase, and ISYS finds the
  16074. word or phrase in the available files. ISYS can read 28 word processor
  16075. formats, as well as some spreadsheet and databases file formats. Documents to
  16076. be searched remain in their native formats. The Developer's Toolkit also lets
  16077. OEMs develop External Access Modules, which lets them extend the functionality
  16078. of ISYS with their own specific data access interfaces. ISYS can then access
  16079. and index "foreign" data sources such as text stored in relational databases.
  16080. The ISYS engine is available for Microsoft Windows and DOS. The engine is
  16081. callable from many languages. Sample code is provided for C, Pascal, and
  16082. Visual Basic. For more information contact Odyssey Development, Inc., 650 S.
  16083. Cherry St., Suite 220, Denver, CO 80222, (303) 394-0091; FAX: (303) 394-0096.
  16084.  
  16085.  
  16086. ParaSoft Announces Insight v1.1 and Inuse
  16087.  
  16088.  
  16089. ParaSoft Corporation has announced Insight v1.1 and Inuse, components of
  16090. ParaSoft's TQS (Total Quality Software) package. Insight v1. 1 is a run-time
  16091. debugger which supports finding the bugs in software after compiling or
  16092. relinking it. Features of Insight v1.1 include: checking for uninitialized
  16093. memory accesses at compile and run-time; incremental checking without
  16094. recompilation; linkable interfaces which let the user extend and customize
  16095. error checking; stack tracing in error reports; error suppression; support for
  16096. the animation of dynamically allocated memory blocks; real-time animation of
  16097. an application's dynamic memory allocation; "Total Coverage Analysis," which
  16098. lets the user see how the code has been tested; and support forX Window System
  16099. v5.0 and Motif v1.2.
  16100. A modular component of Insight v1.1 is Inuse, a graphical utility which
  16101. provides real-time animation of the dynamic memory allocation requests in an
  16102. application. Inuse provides feedback on algorithms which "leak" memory and can
  16103. help the user allocate memory.
  16104. Insight v1.1 supports Sun IBM, DEC, HP, and SGI platforms. Insight is $995.
  16105. For more information contact ParaSoft Corporation, 2500 E. Foothill Blvd.,
  16106. Pasadena, CA 91107, (818) 792-9941; FAX: (818) 792-0819; e-mail:
  16107. insight@parasoft.com.
  16108.  
  16109.  
  16110. Pure Software Releases Purify for HP PA-RISC and Sun Solaris 2.x
  16111.  
  16112.  
  16113. Pure Software Inc. has released Purify for HP PA-RISC workstations and Sun
  16114. SPARC workstations running Solaris 2.x. Purify detects run-time errors in C
  16115. and C++ UNIX applications. Purify uses Pure Software's Object Code Insertion
  16116. (OCI) technology. OCI examines the object code and inserts checking
  16117. instructions around every memory function to monitor usage at run-time and
  16118. report illegal memory accesses and leaks. OCI lets Purify analyze the
  16119. application including shared and third-party libraries. Purify is also
  16120. integrated with HP's SoftBench development environment.
  16121. Purify for HP PA-RISC and Sun SPARC running Solaris 2.x is $1,298 per license.
  16122. For more information contact Pure Software Inc., 1309 S. Mary Ave., Sunnyvale,
  16123. CA 94087, (408) 720-1600; FAX: (408) 720-9200; e-mail: info@pure.com.
  16124.  
  16125.  
  16126. MKS Announces MKS Toolkit v4.2 for DOS
  16127.  
  16128.  
  16129. Mortice Kern Systems Inc. has announced MKS Toolkit v4.2 for DOS. MKS Toolkit
  16130. v4.2 for DOS includes USENET news support for sending and receiving electronic
  16131. news, including nr, a visually oriented mail and news reader. MKS Toolkit also
  16132. includes two Windows applications, Vi for Windows with functional scroll bars,
  16133. font selection, mouse support, and sizable windows; and Visual Diff for
  16134. Windows with several options for viewing files differences. Other features of
  16135. MKS Toolkit v4.2 for DOS include: ASPI-compatible SCSI tape drive support in
  16136. which the pax backup utility conforms to IEEE POSIX.2 standard and supports
  16137. the standard tar and cpio formats; 32-bit utilities; and a program launcher
  16138. for Windows.
  16139. MKS Toolkit v4.2 for DOS is $299. The upgrade is $99. For more information
  16140. contact Mortice Kern Systems Inc., 35 King Street N., Waterloo, Ontario,
  16141. Canada N2J 2W9, (519) 884-2251; FAX:(519)884-8861.
  16142.  
  16143.  
  16144. Blue Sky Upgrades RoboHELP
  16145.  
  16146.  
  16147. Blue Sky Software Corporation has announced its support for Word v6.0 for
  16148. Windows with its upgraded version of RoboHELP. RoboHELP v2.6 is a help
  16149. authoring system for Windows and Windows NT. Features of RobeHELP v2.6
  16150. include: automatically converting existing text into a Help system or a Help
  16151. system into user documentation; creating topics and jumps at any time by
  16152. adding a graphical tool palette to Word v6.0; and creating hotspot graphics by
  16153. placing a bitmap in a Help window. Other features of RoboHELP v2.6 include:
  16154. access to the Windows Help Engine; VBX controls and custom controls; Error
  16155. Wizard; and a Simulation Mode which lets the user test changes without
  16156. recompiling. RoboHELP v2.6 includes the Help compilers, and runs the Help
  16157. compilation under Windows.
  16158. RoboHELP v2.6 is $499. Registered uses for RoboHELP v1.0 can upgrade for $229.
  16159. For more information contact Blue Sky Software Corporation, 7486 La Jolla
  16160. Blvd., Suite 3, La Jolla, CA 92037, (619) 459-6365; FAX:(619)459-6366.
  16161.  
  16162.  
  16163. Computer Mindware Introduces Visual FEDIT v2.7
  16164.  
  16165.  
  16166. Computer Mindware Corporation has introduced Visual Formatted EDIT (Visual
  16167. FEDIT v27). Visual FEDIT v2.7 is a Custom Control Dynamic Link Library that
  16168. supports many data types including: strings, numeric, date, time, Boolean, and
  16169. unformatted multi-line text. Visual FEDIT v2.7 supports VBX format for
  16170. integration into Visual C++ v1.0 or Visual Basic v2.0/3.0 applications, and
  16171. includes a Smalltalk class library, a wrapper for Smalltalk/V.
  16172. Features of Visual FEDIT v2.7 include support for class encapsulation for C++,
  16173. data binding for VB, and read-only fields. Visual FEDIT v2.7 also supports
  16174. required and optional fields, null and default values, auto-validation,
  16175. run-time dynamic reformatting, named fields for links to database fields,
  16176. user's extension flags, and dialog and field level support. Visual FEDIT v2.7
  16177. is $159. Source code is available. For more information contact Computer
  16178. Mindware Corporation, 36 Trinity Place., E. Hanover, NJ 07936, (201) 884-1123.
  16179.  
  16180.  
  16181.  
  16182.  
  16183.  
  16184.  
  16185.  
  16186.  
  16187.  
  16188.  
  16189.  
  16190.  
  16191.  
  16192.  
  16193.  
  16194.  
  16195. We Have Mail
  16196. Dear Dr. Plauger:
  16197. I am writing in response to P.J. LaBrocca's recent article "Dynamic
  16198. Two-Dimensional Arrays" (November 1993 issue of The C Users Journal). On page
  16199. 77 of that article, LaBrocca mentions that his dyn2darray routine suffers from
  16200. limited portability because of memory-alignment problems. I have found a
  16201. method of implementing LaBrocca's two-dimensional array allocation without his
  16202. memory-alignment problem. The method I describe below maintains a crucial
  16203. advantage of dyn2darray, it still requires only one call to calloc. The key
  16204. idea is to determine which memory addresses are suitably aligned for storing
  16205. the objects. The allocated memory is used to store three different data types:
  16206. pointers to void, unsigned integers, and objects of unknown type. This might
  16207. necessitate leaving gaps between objects of different types to achieve proper
  16208. memory alignment.
  16209. I start with the following question: How do we know at which addresses we can
  16210. safely store the objects of the 2-D array?
  16211. Let obj_size = sizeof(object type).
  16212. Let p be a character pointer to dynamically allocated storage.
  16213. C allows us to store a one-dimensional "array" of these objects starting at
  16214. address p. From this it follows that the addresses
  16215. p + k * obj_size (k = 0,1,2,...)
  16216. are properly aligned for storing these objects (as long as we don't go beyond
  16217. the allocated region of memory). For example, if the object has type double,
  16218. sizeof (double) = 8. (Suppose this to be the case on some machine.) Then
  16219. p, p+8, p+16, p+24, ...
  16220. are valid addresses at which to store a double.
  16221. This example shows how to allocate a 2-D array of doubles so that the pointers
  16222. and the objects are stored in the same allocated memory region while
  16223. preserving proper memory-alignment. For the moment I will not worry about the
  16224. added complication of storing the number of rows and columns. Suppose that we
  16225. want to allocate a 2-D array of doubles given the following conditions:
  16226. sizeof (double *) = 2, rows = 3,
  16227. columns = 4, sizeof (double) = 8
  16228. Storage for the pointers uses up 14 characters of memory. The first available
  16229. spot after the pointers at which we can store the doubles is at p+16. This
  16230. means that a gap of two characters must be left between the pointers and the
  16231. doubles to ensure proper alignment of the doubles. In general, we would store
  16232. the objects at the address
  16233. p + DynRndUp(SpaceForPointers, obj_size)
  16234. where
  16235. SpaceForPtrs = rows * sizeof (void *)
  16236. and
  16237. Dyn2dRndUp(i,j)
  16238. is a macro that rounds i up to the nearest multiple of j. (i.e., Dyn2RndUp(14,
  16239. 8) = 16).
  16240. Of course, we still have to consider how to store the number of rows and
  16241. columns between the pointers and objects. The first address at which we could
  16242. store these values is given by:
  16243. p + SpaceBeforeRowsAndCols
  16244. where
  16245. SpaceBeforeRowsAndCols =
  16246. Dyn2dRndUp(SpaceForPtrs,
  16247. sizeof (unsigned))
  16248. The amount of space used by the pointers and the two unsigned values (number
  16249. of rows and number of columns) is:
  16250. SpaceForPtrsRowsAndCols =
  16251. SpaceBeforeRowsAndCols
  16252. + 2 * sizeof(unsigned)
  16253. Finally we can begin storing the objects at the address
  16254. p + SpaceBeforeObjects
  16255. where
  16256. SpaceBeforeObjects =
  16257. Dyn2dRndUp(SpaceForPtrsRowsAndCols,
  16258. obj_size)
  16259. If there is a substantial gap between the end of the pointers and the
  16260. beginning of the objects there may be several locations at which we could
  16261. store these two unsigned values. To make sure that we can recover these two
  16262. values (given only the 2-D array) we must store them as close to the objects
  16263. as possible. An example will clarify the matter. Suppose:
  16264. rows = 3, cols = 2,
  16265. sizeof (void *) = 2,
  16266. sizeof (unsigned) = 4,
  16267. obj_size = 22
  16268. These sizes were chosen to illustrate the most general case. The pointers use
  16269. up the first six characters of memory. The first address available to store
  16270. the number of rows would be p+8. The objects can be stored starting at p+22.
  16271. The first two columns in Figure 1 show that we have two choices as to where to
  16272. store the number of rows and columns.
  16273. In this example, SpaceBeforeObjects = ((char **) p)[0] - p is 22. If you redo
  16274. this example setting rows = 5 you will also get 22 (see third column in the
  16275. diagram above). Therefore, storing the number of rows and columns as close to
  16276. the pointers as possible makes it impossible to recover them later. The first
  16277. and third columns in the diagram above makes it is clear that the location of
  16278. these two values cannot be determined by the amount of space before the
  16279. objects.
  16280. Storing these two values as close to the objects as possible solves this
  16281. problem. We simply round down the space before the objects to the nearest
  16282. multiple of sizeof (unsigned). In the example above (columns 2 and 3) the
  16283. space before the objects (22) is rounded down to the nearest multiple of
  16284. sizeof (unsigned) (4) to get 20. The values for the number of rows and columns
  16285. are to be stored immediately before this offset (p+20).
  16286. In general, the expression
  16287. p + Dyn2dRndDown(SpaceBeforeObjects,
  16288. sizeof (unsigned))
  16289. - 2 * sizeof (unsigned)
  16290. where Dyn2dRndDown(i,j) is a macro which rounds i down to the nearest multiple
  16291. of j, gives us a pointer to the beginning of the row and column data. In the
  16292. above example we would get
  16293. p + Dyn2dRndDown(22,4) - 2 * 4 = p + 12
  16294. I provide new versions of dyn2darr.c and dyn2darr.h [available on the monthly
  16295. code disk -- pjp].
  16296. Finally, I should mention that one small portability problem still remains.
  16297. Both my version of the code and LaBrocca's make the assumption that all
  16298. pointers have the same size and representation. My understanding is that this
  16299. is much less of a portability issue than is the memory-alignment problem. (I
  16300. have seen several C books write "portable" code which makes this assumption).
  16301. P.S. I hope this proves useful to your readers. I do not have my own email
  16302. address but I can be reached at the address shown below, or at jessica @
  16303. engin.umich.edu.
  16304. Steve Coffman
  16305. C-TAD Systems, Inc.
  16306. Boardwalk Office Center
  16307.  
  16308. 3025 Boardwalk Drive
  16309. Ann Arbor, Michigan 48108
  16310. (313)-665-3287
  16311. Whew! I think you illustrate neatly why LaBrocca saw fit to sidestep the
  16312. storage alignment issue. You can also sidestep the problem of different sizes
  16313. of data pointers by storing only pointers to void. Still, beyond a certain
  16314. point, the investment in potential portability starts getting hard to justify.
  16315. -- pjp
  16316. Hi Bill,
  16317. I've just finished reading the November CUJ -- very entertaining as always --
  16318. and I couldn't help notice the inside back page ad:
  16319. Sequiter Software Inc. says, "As with C, ANSI C++ is an international standard
  16320. across all hardware platforms. This means you can port CodeBase++ applications
  16321. between DOS, Windows, NT, OS/2, Unix, and Macintosh -- today."
  16322. Sigh! The BSI jumped up and down on a few advertisers over stuff like this in
  16323. the days before validated C compilers were available. Perhaps someone should
  16324. have a word with the folks at Sequiter?
  16325. See you in San Jose?
  16326. Regards,
  16327. Sean Corfield
  16328. Development Group Manager
  16329. Programming Research, England
  16330. Sean.Corfield@prl0.co.uk (44) 372-462130
  16331. Yeah. People aren't supposed to claim conformance to a standard until it's
  16332. approved. In the case of C++, it's particularly daring to refer to a putative
  16333. "international standard." -- pjp
  16334. Greetings,
  16335. Enough of the language standards and extensions stuff already. Compare and
  16336. contrast:
  16337. Applications: Sequiter Software's CodeBase 5.0 vs Kedwell's DataBoss
  16338. Libaries: Greenleaf's SoftC Database Lib vs Software Science's Topaz
  16339. GO BROWNS!
  16340. Sincerely,
  16341. Noah Hester
  16342. nbh@cis.csuohio.edu
  16343. Your wishes are noted, except for the part about the Browns. -- pjp
  16344. Dear PJP:
  16345. In a letter published in the November 93 CUJ, you mention the problem of using
  16346. sizeof in preprocessor statements, something most ANSI compilers don't allow.
  16347. Mr. Plauger offered a solution:
  16348. static char junk[sizeof(structname) !=132
  16349. ? 0 : 1];
  16350. but also offered the caveat that it wastes a byte of storage. I've been using
  16351. a similar solution for several years that doesn't waste any storage:
  16352. typedef struct {
  16353. // ensure sizeof(structname)
  16354. char x[sizeof(structname) == 132];
  16355. // is exactly 132 bytes.
  16356. } _size_check_structname_;
  16357. Because the statement is a typedof, no storage is allocated. The == operator
  16358. is guaranteed to generate a 0 or 1 result (on an ANSI compiler). Even on a few
  16359. compilers I've encountered which have an extension which allows a zero-sized
  16360. array to appear as the last element in a structure, an error message is
  16361. generated because the size of the structure overall cannot be zero. The error
  16362. message you get from this construct varies between compilers, but it rarely
  16363. indicates what the real problem is, so comments in the code are essential.
  16364. (The fact that the typedef name is _size_check_something_ helps. Using a
  16365. similar standard naming convention throughout a project is probably a very
  16366. good idea.)
  16367. Other checks are possible using this method. For example, in a project once I
  16368. had special 16-byte-at-a-time block zero and block move routines for
  16369. performance. To safely use them on structures, I included the check:
  16370. typedef struct {
  16371. // ensure sizeof(memnode)
  16372. char x[(sizeof(memnode) & 0x0F) == 0];
  16373. // is a multiple of 16 bytes.
  16374. } _size_check_memnode_;
  16375. I've encountered several C programmers who hated such compile-time assertions
  16376. in source (or header) files; perhaps they never make mistakes, or they enjoy
  16377. long debugging sessions. While it is rare to make a mistake which causes one
  16378. of these assertions to fail, the hours saved when it happens are worth the
  16379. minutes it takes to code them.
  16380. Ian Lepore
  16381. Moderator, BIX c.language conference
  16382. ianl@bix.com
  16383. I like your solution better than mine. Thanks for telling us about it. -- pjp
  16384. Dear Sir
  16385. Since a long time ago, I'm an avid reader of the articles that you write in
  16386. Dr. Dobbs, C Users Journal, etc. I enjoy every piece, especially with your
  16387. style of writing.
  16388. I just want to say thanks for your great writing, and for being such a great
  16389. researcher in the computer area (and the like).
  16390. Your Friend,
  16391. Leo Medellin
  16392. 0______LINEEND____
  16393. _.>/)_____LINEEND____
  16394. leo.medellin@asb.com * (_) \(_)....
  16395. leo.medellin%bbs@quake.sylmar.ca.us
  16396. ak467@FreeNet.HSC.Colorado.EDU
  16397. Thanks. And I like your bicyclist. -- pjp
  16398. Mr. Plauger
  16399. Thanks for continuing to produce an interesting magazine. I have been a
  16400. subscriber for about 5 years. Some points/comments:
  16401.  
  16402. 1. Articles such as "Code Capsules" by Chuck Allison are useful -- we must all
  16403. remember that new, young C programmers join the ranks and have missed all the
  16404. useful information contained in the early issues of the Journal.
  16405. 2. Linux -- a Unix System V clone which runs on a PC, costs virtually nothing
  16406. and includes Emacs, Latex, and X together with the GNU C and C++ compilers --
  16407. is becoming very popular. Is there any possibility of some coverage for Linux
  16408. in your magazine?
  16409. 3. Dr Dobb' s Journal have produced a CD-ROM containing all articles from
  16410. January 1988 to June 1993 together with text search facilities. Is there any
  16411. chance that such a product will be produced by R&D as I feel that many
  16412. subscribers would find this of interest.
  16413. Yours sincerely,
  16414. David Richards
  16415. 184 Turf Pit Lane
  16416. Moorside
  16417. Oldham
  16418. OL4 2ND
  16419. ENGLAND
  16420. (1) I like Chuck's writing too. Glad you appreciate the function it serves in
  16421. this magazine. (2) I'll happily entertain proposals for articles in Linux. (3)
  16422. We've been exploring numerous ways to make the material from CUJ more
  16423. available to our readers, but I don't have an answer for you yet on this
  16424. topic. -- pjp
  16425. Dear Mr. Plauger,
  16426. I really enjoy reading The C Users Journal. There is one glaring omission in
  16427. the C++ I/O streams library: a reset manipulator to set the stream back to
  16428. default mode. Any function (except for main) has no knowledge of what flags,
  16429. fill character, and precision are set for any streams it receives from the
  16430. caller. To make sure there are no surprises, it has to explicitely set all the
  16431. flags, the fill character and the precision to the values it needs. Having a
  16432. reinit manipulator would make this much easier. Now let's take it one step
  16433. further: what happens when the function returns control back to the caller.
  16434. The mode of the stream may have changed, and the caller has to re-set
  16435. everything. What a mess! Of course the called procedure should undo any
  16436. changes it has made, so it has to save the mode on entry and restore it on
  16437. exit, adding a couple of lines to every function. It seems obvious that we
  16438. need save and restore manipulators to do the job. Of course you can implement
  16439. the reinit, save, and restore manipulators, but this is such a universal need
  16440. that I don't understand why they're not part of the standard library. Right
  16441. now everyone who uses I/O manipulators has to reinvent the wheel on their own.
  16442. Incidentally, C Standard I/O has a clear advantage here because it's modeless.
  16443. Sincerely,
  16444. Hans Salvisberg
  16445. Salvisberg Software & Consulting
  16446. Bellevuestr. 18
  16447. CH-3095 Berne
  16448. SWITZERLAND
  16449. The nearest thing to what you want in the current C++ library draft is
  16450. ios::copyfmt, which lets you copy just the formating information between one
  16451. ios object and another. -- pjp
  16452. Dear PJP:
  16453. I wish to comment on the article "A Revision Control System for MS-DOS",
  16454. published in the July 1993 issue of The C Users Journal. There are two errors
  16455. that will cause people a lot of grief. The function print_warning listed on
  16456. page 48 declares the variable string as a character pointer, but doesn't
  16457. assign it a value. It is then used in a call to fgets as the buffer location.
  16458. This will lead to the data fgets reads being written to who knows where, and
  16459. may cause serious problems. It caused my system to re-boot. The same type of
  16460. error exists in the function rev_number, listed on page 50.
  16461. Another concern I have about the code presented in the article is the lack of
  16462. checks for unexpected end-of-file conditions. The first thing I put under RCS
  16463. control, after fixing the above mentioned fault, was the RCS source code. I
  16464. believe I then used checkout to get a copy of a file, and my system hung. The
  16465. reason the system hung was that the editor I used to create the source files
  16466. did not require that the file end with a newline character, so the RCS file
  16467. did not end with a line containing the delimiter, but with a line containing
  16468. the } character followed by the delimiter. Since there were no checks for EOF
  16469. on the input file, the system kept calling fgets to get the next line, and the
  16470. check for the delimiter always failed.
  16471. I also worry about the lack of checks for write failures. It appears that
  16472. there could be serious problems of writes are attempted and the disk is
  16473. already full, though I must admit, I have not seen this problem.
  16474. J.P. Schoonover
  16475. (708) 979-7907
  16476. It is always interesting to see what you have to do to code prepared for
  16477. presentation when you start using it seriously. Or code tested on one system
  16478. when you move it to another. -- pjp
  16479. Dear PJP:
  16480. Over the years I've gotten much useful code and advice from CUJ. However,
  16481. lately the quality of the published code has decreased significantly.
  16482. As an example, consider the last two articles on exceptions CUJ has published.
  16483. While I do not wish to single out these authors, neither of these packages
  16484. compiled without significant modification on any popular workstation or PC
  16485. operating system, nor worked as advertised once compiled. In addition, neither
  16486. package (on the code disk or as available from the Internet) included any
  16487. installation instructions. It seems obvious the articles were accepted on the
  16488. basis of perceived interest and not the portability or functionality of either
  16489. package.
  16490. I think all but the most basic of packages offered should include installation
  16491. instructions and dependencies. Both exception packages have substantial
  16492. requirements for non-standard development packages (such as a specific version
  16493. of gmake). Perhaps a "tools and rules" sidebar containing instructions for
  16494. building and using the code would solve this problem.
  16495. A more significant problem is the poor performance and portability of the
  16496. code. While I would not expect production or GNU quality code from CUJ, the
  16497. functionality advertised should be present and hopefully relatively bug free.
  16498. I was especially disappointed with the most recent package because of the
  16499. attractiveness of an exception mechanism portable between C and C++. After
  16500. much work by myself and the author I was able to compile this package but then
  16501. discovered substantial run-time problems. Test cases were not present and the
  16502. samples provided with the package would not even compile -- due to undefined
  16503. symbols rather than any obscure portability issue. I commend the author for
  16504. all the help he gave me but why did CUJ publish a package seemingly without
  16505. looking at the source or attempting to build it? Good examples of previous
  16506. high quality CUJ articles include the socket library and generic object
  16507. packages. Both were simple enough to compile on any operating system offering
  16508. TCP/IP services or a C++ compiler and are robust enough to have become a
  16509. standard part of my programming toolbox. Both of the articles I've singled out
  16510. offered functionality of great usefullness but neither delivered on their
  16511. promises nor did the articles contribute substantially to the understanding
  16512. you could gain from a quick reading of any number of C++ or EIFFEL books.
  16513. If CUJ is to be a pragmatic magazine for professional programmers and not a
  16514. fluff publication or academic journal a la Communications, its offerings
  16515. should set the standard for well-executed, portable code. Professionals as
  16516. well as beginners could benefit from the example such a publication would set.
  16517. C. Justin Seiferth
  16518. Phillips Laboratory
  16519. (505) 846-0561 (V)
  16520. (505) 846-0473 (F)
  16521. seiferth@lyra.plk.af.mil
  16522. We do indeed make some effort to pick articles that have code which is both
  16523. useful and reasonably correct. Sadly, we (I) don't always guess right. And we
  16524. lack the resources to compile and test all the submitted code, or even verify
  16525. that they are easy to install and run cursory examples. I wish it were not so.
  16526. On the other hand, your experience with one particular author is not unique.
  16527. Often, our readers tell us that authors make extraordinary efforts to assist
  16528. potential users of their code. I am pleased that our contributors are so
  16529. willing to follow through on their submissions.
  16530. Both this and the preceding letter underscore the essential problem of using
  16531. other people's code. There is a tremendous variation in robustness,
  16532. portability, and ease of use. I'm not casting aspersions on the talents of our
  16533. contributors when I say this -- what is a good design decision for one person
  16534. may be an incredibly poor decision for someone else. We can only hope that
  16535. most of the articles we run are useful to many of our readers much of the
  16536. time. We'll keep trying. -- pjp
  16537. Dear Mr. Plauger,
  16538. In the August 1993 C Users Journal article "Automated Unit Testing," Mr.
  16539. Meadows lists several guidelines he recommends. The first is "Include all test
  16540. code inside a main program, that is, inside a #ifdef TESTMAIN block."
  16541. Well, that approach just does not work that well. Having developed and
  16542. maintained several large products over a number of years, I have found it
  16543. better to have truly independent test stubs. Aside from not having lint
  16544. complain about multiple mains, having one test program that exercises all the
  16545. functions in a library is much more useful and compact. In addition, golden
  16546. output of each test function is easier to manage if it is maintained in files
  16547. along with the library.
  16548. A library (or application) code area is then composed of Source Code, Make
  16549. files, a regression test script, and golden unit test case input and output
  16550. files. This is all maintained in the RCS pool along with the source. Although
  16551. we find it easier to keep the unit tests with the source code, if disk space
  16552. is a problem they can be maintained separately.
  16553. This does violate another guideline of Mr. Meadows. "Do not make the test
  16554. program dependent on external files." Well, sorry. But any sufficiently large
  16555. system will have some external files. Libraries will rely on other libraries.
  16556. And applications will have large external data files. Having some simpler
  16557. versions of the data files for unit tests is not a bad tradeoff.
  16558. We find, as a general rule of thumb, that our library unit test cases are at
  16559. least as large as the code itself. When one adds in the test stubs, and golden
  16560. output files, it does build up quickly. Some Application Unit Tests can grow
  16561. even larger, say 2-5x.
  16562. However, the payback is when any developer can go into a library or
  16563. application, run make test, and in a few minutes see if their changes have
  16564. affected any previous results. Given that 20% of a 150,000 line software
  16565. product may be changed during a given release, this pays off very quickly in
  16566. not introducing unwanted bugs. The costs of this approach are disk space, and
  16567. the displine to maintain the tests as part of the source and development
  16568. process.
  16569. On a final note, one of our newer tools has been the Purify software from Pure
  16570. Research. Even in evaluation, the product was able to find several memory
  16571. leaks and other problems which had gone undiscovered for years. I personally
  16572. recommend this group of products for any serious software team.
  16573. Sincerely,
  16574. Richard Vireday
  16575. Sr. Software Engineer, Intel
  16576. rvireday@pldote.intel.com
  16577. (916)351-6105
  16578. I've found the approach described by Meadows very useful for smaller projects,
  16579. and the approach you describe better for larger ones, for the reasons you
  16580. describe. -- pjp
  16581. Dear Mr. Plauger,
  16582. While I can't claim your longevity in data processing, I have been in the
  16583. industry since 1976. As you have pointed out, there's little in the world of
  16584. data processing that hasn't been seen before. In particular, there have always
  16585. been people who believe it is possible to constsruct a perpetual motion
  16586. machine for software support. Once you prime the pump with an initial license
  16587. fee, the machine keeps producing answers, bug fixes, and enhancements with no
  16588. further input. This belief is reinforced by the examples of Word Perfect
  16589. Corporation and Microsoft, who seem to keep providing support just because
  16590. they think it's the right thing to do.
  16591. Free support is really a variation on the infamous Ponzi scheme; you give me
  16592. $1,000 and I will pay you $250 interest every month forever. Or, to rephrase,
  16593. you buy my $125 competitive software upgrade, and I will pay the distributor
  16594. his cut and provide you with $45 per hour support forever. It's become the
  16595. case for PC software, including the development tools advertised throughout
  16596. your magazine, that selling computer software in an extended market requires
  16597. the vendor to either lie or become a software missionary.
  16598. This leaves potential customers only two ethical and legal purchase
  16599. alternatives: only buy products from vendors who charge enough to cover
  16600. support, or accept spotty support provided for free. Anyone who steals
  16601. software should never become a parent, or should have a high tolerance for
  16602. hypocrisy when their child is found cheating or shoplifting.
  16603.  
  16604. Sincerely,
  16605. James P. Hoffman
  16606. 416 West Kerr St.
  16607. Salisbury, NC 28144
  16608. While I wouldn't use your emotion-charged phraseology, I agree with much of
  16609. what you say. I ran a software company for a decade and found myself
  16610. entertaining a different scheme for pricing code and maintenance almost every
  16611. year. Charge too much and your competitors steal the market. Charge too little
  16612. and you go broke getting rich. I'm glad I don't run a software company today.
  16613. -- pjp
  16614. Dear Mr. Plauger:
  16615. I want to express my thanks for the three part series CUJ ran on pointers by
  16616. Chuck Allison. These are the kind of articles that are so helpful to me.
  16617. Incidentally, they exemplify what is missing from most books on C that Mr.
  16618. Musielski complained about in his letter in the October issue. But to Chuck
  16619. Allison's articles: I was raised on the assembly language programming and did
  16620. nothing else for the first ten years of my programming experience.
  16621. Consequently, I was well aware of the advantages of indirect addressing, but
  16622. it has been amazing to me how little this benefited me understanding C's
  16623. pointer syntax.
  16624. I have a library of 38 books on C. Yet it is so often when I run into a
  16625. problem that I must wade through more than half of them before I discover the
  16626. key. It is a constant annoyance that most books on C never proceed beyond the
  16627. simplest example. I will give you a trivial example: look at most books aimed
  16628. at beginners in C. How many show that curly brackets are necessary with if,
  16629. for, do, while when more than one statement follows? Trivial, maybe, but not
  16630. to a beginner. How many warn that scanf is worthless for dealing with user
  16631. responses that don't meet the programmed format requirements? How many show
  16632. useful alternatives for interactive user responses?
  16633. My first exposure to higher-level language was BASIC. I have exactly three
  16634. books explaining the language and never needed more. Mike Musielski has a
  16635. valid grievance and it is just a little unsettling to me that I have had to
  16636. collect so many books on C despite my admiration for C and appreciation of its
  16637. features and power.
  16638. On the subject of Numerical Extensions to C, about which you wrote in the
  16639. September issue: My interest in C mainly centers on electronic engineering
  16640. programming and I watch with great interest the deliberations of the NCEG
  16641. group. One feature which I have seen no information is whether they are
  16642. looking at non-integer exponentiation. BASIC allows statements such as 2^1.6
  16643. which save a lot of bother working with logarithms. The only text I have found
  16644. dealing with engineering programming is Numerical Recipes in C. Unfortunately,
  16645. the authors worked assiduously at a FORTRAN translation and mostly ignored the
  16646. powerful features of C because no counterpart existed in FORTRAN. The result
  16647. is that oft-times the sources are not easy to read. I often fall victim to
  16648. their "unit" approach to arrays where they simulate FORTRAN's elimination of
  16649. the zero element of an array.
  16650. Sincerely,
  16651. Forrest Gehrke
  16652. 75 Crestview Rd.
  16653. Mountain Lakes, NJ 07046
  16654. There are so many books on C simply because there is a huge market. Everybody
  16655. wants to write the next Kernighan & Ritchie (still the best selling technical
  16656. book ever), and nobody wants to leave an entire market to some other potential
  16657. K&R.
  16658. As for your question about exponentiation, it seems to me that the current pow
  16659. function does what you want. -- pjp
  16660. Mr. Plauger,
  16661. With much amusement, I read your article "An Embedded C++ Library" in the
  16662. October 1993 issue of Embedded Systems Programming. In the past, standards and
  16663. embedded systems were always separate subjects. They did not benefit from each
  16664. other. Now that you are an official EPSILON [Embedded Programming Society,
  16665. International, and the Loyal Order of Nonentities -- pjp] author I am glad to
  16666. see that you want to become part of the solution rather than part of the
  16667. problem. By way of your first paragraph in the Embedded Wish List section of
  16668. your article, I see that you understand that the major problems are
  16669. non-technical. Welcome and glad to have you on board.
  16670. I have some experience trying to get the ANSI C standards committee to provide
  16671. language support for embedded systems. For the most part, the ANSI C committee
  16672. was a very homogeneous group of compiler writers whose expertise in embedded
  16673. systems was unhindered by their ignorance of the subject. I tried to make some
  16674. of your points at one of their meetings. They literally laughed at me. The
  16675. chair of the subcommittee addressing these issues openly equated embedded
  16676. systems with toasters. As a group, they were arrogant, rude, and lacked the
  16677. experience to understand the technical issues except from the standpoint of
  16678. compiler design. For the most part, they seemed to prefer it that way.
  16679. It reminds me of the story of the salesman and the engineer or safari in
  16680. Africa. The first morning, the salesman took the engineer lion hunting.
  16681. Shortly after they left the hut, the salesman and the engineer came running
  16682. back with a lion snapping at their heels. As they reached the hut the salesman
  16683. opened the door and stepped aside as the engineer and then the lion ran into
  16684. the hut. Slamming the door shut the salesman bragged, "I caught him, now you
  16685. skin him."
  16686. Instead of dealing with embedded systems issues, the ANSI C committee slammed
  16687. the door and was done with the problem. It is the embedded systems programmers
  16688. who are constantly reminded that they are still living with a lion. I hope the
  16689. C++ salesman knows more about catching lions.
  16690. Years ago, at your request, I sent you copy of my public comments on ANSI C by
  16691. fax. Let me paraphrase a few key recommendations.
  16692. Freestanding libraries must be a superset not a subset of the hosted library
  16693. requirements because embedded systems requirements are more strict.
  16694. Functions that require operating systems support must have a defined interface
  16695. to the operating system. For example, in the freestanding library, printf
  16696. should call putc. The library should include printf and the user should write
  16697. putc. The library should also provide a putc that acts a data sink, in case
  16698. the user written putc is not available. The standard should document both
  16699. functions as well as the relationship between them. In some cases, the
  16700. standard will need to define new freestanding user written functions. For
  16701. portability all library functions must be present in the freestanding
  16702. environment without exception.
  16703. The standard needs to define which freestanding library functions you may call
  16704. recursively and which you may not call recursively.
  16705. Now we get to deal with the C++ standard. I hope history will not repeat
  16706. itself. I wish you lots of luck, fortitude and the tenacity to get the job
  16707. done.
  16708. Yes, I know your article appeared in another magazine. The other magazine is
  16709. deficient. It lacks a Mail column. I suppose it has something to do with
  16710. committing space to advertisers or readers depending upon where your
  16711. priorities lie. I like The C Users Journal. It has lots of mail with many
  16712. differing and even critical viewpoints. Since you write for both magazines and
  16713. because this is a topic with very significant impact on readers of both
  16714. magazines, please forgive me the sin of mentioning the name of another
  16715. publication. This is just my way of trying to get broader support for making a
  16716. better language.
  16717. Sincerely,
  16718. Russell Hansberry
  16719. 171 Whitney Road
  16720. Quilcene, WA 98376-9629
  16721. Telephone: 206 765-4465
  16722. Fax: 206 765-4430
  16723. Compuserve ID: 70314,1506
  16724. I'm really sorry you left that C Standards meeting feeling the way you did. I
  16725. can assure you that Jim Brodie as Chair would not tolerate such open
  16726. expressions of disdain as you perceived. (He is personally incapable of being
  16727. rude to another person, in my experience.) There was expertise on embedded
  16728. systems within X3J11 and we had discussed any number of such issues in earlier
  16729. meetings. My article in DDJ expressed my dismay that we chose to provide so
  16730. little explicit support for embedded programming in Standard C, but it was not
  16731. intended as a criticism of the committee's decisions.
  16732. I agree that the level of standardization you describe would be helpful in
  16733. many circles. From experience, however, I know how much work it takes to flesh
  16734. out what you propose. And I know the burden it would impose on most
  16735. implementors to conform. So I expect that, while some things will be better
  16736. attended to in the C++ standard for embedded programming, the final result
  16737. will still fall short of your guidelines. -- pjp
  16738. Dear PJP,
  16739. Thanks for the informative article [on what? can someone guess what this
  16740. refers to and insert its name and publication date? - pjp]. Incidentally, the
  16741. October issue of Byte carries two excellent articles on similar products,
  16742. which are more expensive, yet seem to do the same thing: CodeCenter,
  16743. ObjectCenter, and others (page 28) and the heavily-advertised and consequently
  16744. high-priced BoundsChecker (page 159). (I suggest, CUJ acquire a copyright and
  16745. carry those articles in December, for the benefit of all readers.)
  16746. Isn't it a shame (for the C language compiler manufacturers) that such
  16747. products are necessary? I understand that in the early days of C, products
  16748. like the manifold LINTs were necessary. There was memory either for a compiler
  16749. or a LINT-type consistency check. But nowadays, our compiler manufacturers go
  16750. after a fashionable C++ compiler, allowing their C-compiler to gather dust.
  16751. Can't someone produce a decent C compiler which catches those memory
  16752. overwrites etc. in a "debug" or "verbose" mode? And then give us decent longs
  16753. which can be used for business applications also (the "pennies in long doubles
  16754. problem")? You mentioned in your October editorial that people are demanding a
  16755. revision of the C standard. You seemed surprised, after having jumped onto the
  16756. C++ wagon in a hurry. You made me curious. Can you tell us more?
  16757. Ref: C++ vs.C
  16758. I have learnt that mediocre standards are better than brilliant ideas, which
  16759. turn the users into guinea-pigs for testing one revision after another. See
  16760. the PC, which in its days was not the most brilliant, yet... See C nowadays.
  16761. It is alright for an academician to buy a C++ compiler. But what if you
  16762. produce a "ton" of software in C++, knowing that you will adapt your code by
  16763. July 1994, when the new standard arrives hopefully. The guinea-pigs are
  16764. necessary to fund the development effort, but not everyone can afford... I
  16765. think the field is maturing. We don't believe anymore that the latest is the
  16766. best. In a different field, take the software developers (WordPerfect e.g.)
  16767. who deliberately shun Windows, as their own graphics routines under DOS are
  16768. superior! (Going by BYTE October 1993.)
  16769. Suggestion: Devote a topical issue of CUJ to the C++ vs. C debate, and discuss
  16770. -- for the benefit of your readers -- the merits of the one over the other.
  16771. The basics. Not the style you adopt when you talk to colleagues on the C++
  16772. committee. You get my idea. I am particularly interested in the performance
  16773. payoff for the interpretative elements in C++ ("housekeeping" known from good
  16774. olde BASIC back in ..., with the deletion of runtime objects). Everyone knows
  16775. by now that programs consist of data and algorithms. C hinges everything on
  16776. algorithms, so that data are ubiquitous unless you deliberately implement some
  16777. "information hiding." C++ sees data only (or mainly) and subordinates
  16778. algorithms to data. It is the other extreme, certainly more appropriate for
  16779. big projects, but hardly suitable for the small application and hardly
  16780. suitable for top-down design. Once you know what you want it is alright to
  16781. pull the objects together, which you have collected in a bottom-up approach.
  16782. The shift in paradigm is total...
  16783. Organizing code is very much like organizing companies. (I have worked in that
  16784. field called management consultancy for some 10 years!) In the small company
  16785. the boss looks after everything. No need for information hiding. Then come the
  16786. specialized departments. In C++ these are the objects. C++ uses structures to
  16787. bundle data and algorithms. What if C was (made) a little more aware of
  16788. subdirectories? (Yes the subdirectories of the operating system.) That is also
  16789. a way of organizing data. It can be done, even with the current
  16790. implementations of C, which know about an INCLUDE directory and the
  16791. source-code directory, at best. The root directory is the boss. It just does a
  16792. few strategic calls to the subdirectories visible from the root directory (I
  16793. am talking development time). The visibility of data is restricted to a
  16794. subdirectory. Between subdirectories there are no side-effects: all data is
  16795. passed as arguments or returned as return values. I have been organizing my
  16796. programs like that for some time. With enormous benefits. I plug and play,
  16797. even without C++ constructors and destructors. The stack is a fabulous
  16798. automatic constructor/destructor. And there is no need for housekeeping. We
  16799. are talking about two languages, which unfortunately are "marketed" as
  16800. successors of each other! What confuses everyone is that in C++ you can
  16801. program (almost) just as if it were good old C!
  16802. You get my message? Give us a decent C, with llongs 64-bits wide (for business
  16803. applications, accounting, and the like), with decent compilers that can check
  16804. for memory overwrites in a verbose mode, with some evolution (not revolution)
  16805. on information hiding, and then move to C++, Smalltalk, when the job is done.
  16806. We are judging a half-finished product against another half-finished product.
  16807. That obscures everybody's judgement. And in turbid waters there is good
  16808. fishing (marketing). Remember: "With our modern marketing techniques we would
  16809. easily have pushed Beethoven's symphony production into the double-digit
  16810. bracket".
  16811. Sincerely yours,
  16812. L. Engbert
  16813. Engbert UB
  16814. Taunusstrasse 8
  16815. 61389 Schmitten
  16816. Germany
  16817. (06084) 2367
  16818. FAX +49-6084-2458
  16819. As a rip-roaring pragmatist, I applaud that the world has both C and C++
  16820. compilers in it. And that more often than not, the two more or less play
  16821. together. Right now, I view C as the safe and solid bet for delivering serious
  16822. applications with serious robustness and performance requirements. I view C++
  16823. as the cauldron in which ambitious new ideas are being stewed. There's a place
  16824. in my tool chest for both. That seems to be the case for many of our readers
  16825. as well. -- pjp
  16826. Dear Mr. Plauger:
  16827. In reading your July 1993 issue, I noticed a letter to the editor from Kevin
  16828. Nickerson asking about distribution of the ANSI C (IS0) Standard. While not in
  16829. machine-readable form, The Annotated ANSI C Standard, recently published by
  16830. Osborne/McGraw-HiII, contains the ANSI C Standard on left hand pages with
  16831. annotations by C programming author Herbert Schildt on right hand pages. This
  16832. book is now available in bookstores or by calling 1-800-227-0900 at a price of
  16833. $39.95.
  16834. We published the book because we felt that there are thousands of people like
  16835. Mr. Nickerson who would like to have the Standard, but don't have their own
  16836. copy. As a special offer to your readers, Osborne/McGraw-Hill will give a ten
  16837. per cent discount off the cover price to those who call our 800 number and say
  16838. they saw it in The C User's Journal.
  16839. Sincerely,
  16840. J. M. Pepper
  16841. Editor-in-Chief
  16842.  
  16843. Osborne/McGraw-Hill
  16844. 2600 Tenth Street
  16845. Berkeley, CA 94710
  16846. 510-548-2805
  16847. FAX 510-54906693
  16848. Here's your chance to get a good price on a useful document. -- pjp
  16849. To the Editor:
  16850. The code from the October 1993 issue shown in Listing 1 was compiled with
  16851. Borland C++ (Chuck Allison's "Code Capsules: Pointers, Part 3"). It does not
  16852. work with the input
  16853. sortargs *.c
  16854. as alleged, but does sort command line arguments if all are entered at the
  16855. command line. It does not work with redirection or using more.
  16856. Any Comments?
  16857. Yours Sincerely,
  16858. James R Lane
  16859. 13 Waratah St.
  16860. Walkerville 3959
  16861. Victoria
  16862. Australia
  16863. Chuck Allison is obviously speaking UNIX shell language here, not DOS command
  16864. lines. The UNIX shell expands wildcards such as *.c and invokes the command
  16865. with the argument list spelled out completely. DOS passes on the wildcard as a
  16866. single argument and leaves it to each command to expand the wildcard as it
  16867. sees fit (if at all). Redirection has no effect on the interpretation of
  16868. command-line arguments. -- pjp
  16869. Dear P.J. Plauger:
  16870. I have a couple of important comments about "Dynamic Two-Dimensional Arrays,"
  16871. by P.J. LaBrocca in the November '93 issue: VERY! USEFUL!
  16872. His dynamic 2-D arrays slipped painlessly into an application that desperately
  16873. needed them. Exactly the kind of information I read your magazine for.
  16874. Jeffrey Siegel
  16875. Tokyo, Japan
  16876. Thanks. Glad we could be of help.-- pjp
  16877. Dear Mr. Plauger,
  16878. This is a response to the letter from Lawrence H. Hardy in CUJ, December 1993.
  16879. A readily available source of documentation of the PCX file format is Flights
  16880. of Fantasy, by Christopher Lampton, Waite Group Press, 1993. This book is both
  16881. fun and informative. It contains C++ source for a flight simulator. It does a
  16882. good job of explaining C++ classes, VGA graphics, and animation.
  16883. Steve Robison
  16884. Thanks. -- pjp
  16885. Gentlemen:
  16886. I am writing in response to a couple of letters in the December 1993 issue of
  16887. The C Users Journal about the Hayes AT Command set. An excellent reference is
  16888. the Technical Reference for Hayes Modem Users, Hayes Microcomputer Products,
  16889. 1992. This publication is available free to Hayes modem purchasers when
  16890. requested within 90 days of purchase of a Hayes modem. Others can purchase
  16891. this publication from Hayes for $25.00. They accept prepayment by check and
  16892. will take Visa and MasterCard or they will send C.O.D.
  16893. Hayes Microcomputer Products, Inc.
  16894. Attention: Customer Service
  16895. P.O. Box 105203
  16896. Atlanta, GA 30348-9904
  16897. (404) 441-1617
  16898. Sincerely,
  16899. James E. Truesdale
  16900. Systems Analyst
  16901. jbm electronics
  16902. 4645 LaGuardia
  16903. St. Louis, MO 63134-9906
  16904. Thanks. -- pjp
  16905.  
  16906. Listing 1 Sorts command line arguments with qsort
  16907. /* sortargs.c
  16908. * from C users Journal p84 Oct 1993
  16909. * Chuck Allison Listing 1 sortargs.c
  16910. * sorts command line arguments with qsort
  16911. *
  16912. */
  16913.  
  16914. #include <stdio.h>
  16915. #include <string.h>
  16916. #include <stdlib.h>
  16917.  
  16918. int comp(const void *,const void *);
  16919.  
  16920. main(int argc, char *argv[])
  16921. {
  16922.  
  16923. qsort(argv+1,argc-1,sizeof(argv[0]),comp);
  16924. while(--argc)
  16925. puts (*++argv);
  16926. return 0;
  16927. }
  16928.  
  16929. int comp(const void *p1,const void *p2)
  16930. {
  16931. const char *ps1 = * (char **)p1;
  16932. const char *ps2 = * (char **)p2;
  16933.  
  16934. return strcmp(ps1,ps2);
  16935.  
  16936. /* End of File */
  16937.  
  16938.  
  16939.  
  16940.  
  16941.  
  16942.  
  16943.  
  16944.  
  16945.  
  16946.  
  16947.  
  16948.  
  16949.  
  16950.  
  16951.  
  16952.  
  16953.  
  16954.  
  16955.  
  16956.  
  16957.  
  16958.  
  16959.  
  16960.  
  16961.  
  16962.  
  16963.  
  16964.  
  16965.  
  16966.  
  16967.  
  16968.  
  16969.  
  16970.  
  16971.  
  16972.  
  16973.  
  16974.  
  16975.  
  16976.  
  16977.  
  16978.  
  16979.  
  16980.  
  16981.  
  16982.  
  16983.  
  16984.  
  16985.  
  16986. Symbolic Access To Embedded Controllers
  16987.  
  16988.  
  16989. Odd A. S. Olsen and Petter H. Heyerdahl
  16990.  
  16991.  
  16992. Odd A.S. Olsen and Petter H. Heyerdahl received their Master of Engineering
  16993. degrees in Engineering Cybernetics at the Norwegian Institute of Technology,
  16994. where Mr. Olsen later received a Ph.D. He is currently an independent
  16995. consultant, developing electronics and programs for embedded controllers. Mr.
  16996. Heyerdahl is associate professor at the Agricultural University of Norway,
  16997. Department of Agricultural Engineering. Odd may be contacted at Jutulveien 11,
  16998. N-0852 Oslo, Norway or by the Internet: odd. olsen@itf.nlh.no.
  16999.  
  17000.  
  17001.  
  17002.  
  17003. Introduction
  17004.  
  17005.  
  17006. A PC often functions as the user interface, debugging tool, and general
  17007. supervisor of an embedded controller. With this arrangement it is important
  17008. for the programs that transfer parameters between the PC and the controller to
  17009. maintain agreement on the names and meanings of all variables. If the PC sets
  17010. parameter 63 to the value of 123, for instance, the PC and the controller must
  17011. agree on what parameter 63 is and what the value 123 represents. This
  17012. consistency is especially at risk during development because new parameters
  17013. are sometimes introduced on-the-fly and often end up in different locations on
  17014. the two computers. The processors may not even agree on a format for the
  17015. values. (An Intel PC could be storing numbers in big-endian format while a
  17016. Motorola controller might want them in small-endian format.)
  17017. A symbol table is an efficient solution to the consistency problem because it
  17018. allows the data structures of the PC and controller to develop separately, yet
  17019. links the two systems with a simple communication protocol. By this method,
  17020. which resembles the message format found in the general-purpose interface bus
  17021. (GPIB), values are referenced by their symbolic name, e.g. measured_pH,
  17022. heater_power etc. We used this principle to control a group of bioreactors.
  17023. Each reactor is controlled by an embedded controller which communicates with a
  17024. supervising PC for operator instructions and data logging.
  17025.  
  17026.  
  17027. How Things are Connected
  17028.  
  17029.  
  17030. Figure 1 illustrates the information flow in the system. In our implementation
  17031. the PC is always the initiator of communication and the embedded controller
  17032. only responds to received messages. The most important entries in the symbol
  17033. tables are the parameter names and their values. (In this context a parameter
  17034. is more general than a constant. It can also represent measured data, debug
  17035. information, etc.) Parameter values move from the PC symbol table to the
  17036. controller symbol table through messages sent over the RS-232 line. The PC
  17037. symbol table can be altered either by accessing the user interface or by
  17038. causing the PC to read a refreshed version of the parameter file. The new data
  17039. is then sent to the symbol table in the embedded controller. The controller
  17040. symbol table can thus be changed from the PC, or through the measurement and
  17041. control algorithms of the controller itself. Parameters for the control
  17042. algorithms are stored in the EEPROM, enabling the controller to initialize its
  17043. symbol table values at start-up and maintain its control process by itself
  17044. should the PC fail.
  17045.  
  17046.  
  17047. The Symbol Tables
  17048.  
  17049.  
  17050. Both the PC program and the embedded controller program are centered on their
  17051. respective symbol tables. The structure of the PC symbol table is:
  17052. typedef struct {
  17053. char *name;
  17054. int ival;
  17055. double fval;
  17056. void *(*scalefunc)(void *);
  17057. char eeProm;
  17058. char file;} symTabEntry;
  17059. Each of the process variables maintained by the controller (temperature,
  17060. oxygen level, pH, etc.) is represented as a record in the symbol table. Every
  17061. record in the table contains a unique name (e.g. measured_pH), an integer or
  17062. double floating-point value, and a pointer to an optional scaling function.
  17063. The table also contains two flags: eeProm and file. If eeProm is set to TO_EE,
  17064. the record will be transferred to the controller upon start-up, and will be
  17065. stored in the controller EEPROM if its value is different from the one already
  17066. residing there. The file flag indicates whether the current record contains
  17067. data associated with the system's parameter file. This parameter file contains
  17068. parameter names (of process variables and other system variables) and their
  17069. values. If the system finds a particular parameter name in the file during
  17070. startup, the system copies the parameter's value into the record, and sets a
  17071. bit in the file flag. Likewise, if the user later wants to save the current
  17072. parameter values back to the file, the system will save only those parameters
  17073. whose file flags are set. While the system is operating, a record holds a
  17074. parameter's value in either ival (for integer parameters) or fval (for
  17075. floating-point parameters).
  17076. The user interface is also oriented to the symbol table. The user can change
  17077. parameters by choosing a parameter window. Each parameter entry field knows
  17078. the pointer to its entry in the symbol table. It can thus read and write the
  17079. value and call the scaling function. When the user changes a parameter, the
  17080. new value is immediately scaled from floating-point to integer and transmitted
  17081. to the controller. Because values are referred to by name rather than by
  17082. pointer, the set-up of the parameter windows is simplified.
  17083. The symbol tables are alphabetically sorted, which allows the processor to use
  17084. a binary search when looking for a name. (A binary search is faster than a
  17085. linear search, but if the tables were large, I would consider using
  17086. hash-tables.) The programmer will now and then insert new entries to the
  17087. tables at a wrong position. On start-up both the PC and controller programs
  17088. therefore go through their tables and check for alphabetic order. If unordered
  17089. elements are found, the function issues an error message.
  17090. The structure of controller's symbol table is
  17091. typedef struct {
  17092. char *name;
  17093. int ival;
  17094. char *(*func)(char *);
  17095. int eeOffset;} symTabEntry;
  17096. name is the name of the parameter, ival the value, and the function pointer
  17097. either a function that can be invoked by a message or a hook for individual
  17098. processing of received parameters. eeOffset is the EEPROM address where the
  17099. parameter is stored. (Parameters that are not saved in EEPROM have an offset
  17100. value of --1.)
  17101. The controller's program runs periodically, e.g. every five seconds. The
  17102. program first reads all measurements and stores the values in the symbol
  17103. table. The control algorithms then take their values and parameters from the
  17104. symbol table and calculate responses. The results are stored into the table
  17105. and output to actuators. Both measured values and actuator states are thus
  17106. available to the PC.
  17107. To understand the use of the symbol table, say the control algorithm is
  17108. pHerror=pHsetpoint-pHmeasured. This can be programmed as:
  17109. FindSymbol ("pHerror")->ival=
  17110. FindSymbol ("pHsetpoint")->ival
  17111. - FindSymbol ("pHmeasured")->ival;
  17112. FindSymbol returns a pointer to the symbol table entry with the given name.
  17113. However, less typing and a faster program are achieved by initializing
  17114. pointers to the symbol table entries at start-up:
  17115. int *ppHerror;
  17116. ppHerror= &FindSymbol ("pHerror")->ival;
  17117. etc. and coding the algorithm as
  17118. *ppHerror= *ppHsetpoint - *ppHmeasured;
  17119. At start-up the symbol tables are initialized according to the following
  17120. sequence. First the tables are initialized with the values specified at
  17121. compile time. The controller then reads values from the EEPROM into its table.
  17122. The PC reads a parameter file, which may change some of the values in its
  17123. table. The PC program will then transfer those values having flag TO_EE set to
  17124. the controller EEPROM and symbol table.
  17125.  
  17126.  
  17127.  
  17128. Message Format
  17129.  
  17130.  
  17131. The messages between the PC and controller are ASCII strings sent over an
  17132. RS-232 connection. Before transmission the message assembler adds a checksum.
  17133. The receiver checks and removes the checksum from the message before passing
  17134. the message to the message interpreter.
  17135. Each message consists of one or more submessages. The submessages and their
  17136. elements can be separated by spaces and have the following format:
  17137. <name><operator>[<value>]
  17138. The name field is the symbolic name of a parameter or a command. The operator
  17139. is a single character as given in Table 1. For messages with an associated
  17140. value, the value field is the numerical value of the parameter. The name must
  17141. begin with a letter (a-zAZ), followed by a number of letters, digits, or
  17142. underscores (a-zAZ0-9_). In our implementation the value field is always an
  17143. integer because all processing in the controller is integer based.
  17144. To add other value types the programmer need only define new operators and
  17145. modify the structures and programs accordingly. A floating-point type is
  17146. useful in some systems. A string type can also be of use, for example to
  17147. transfer strings directly to an operator display on the controller. In a
  17148. multi-processor embedded system, the string type can be used to transfer
  17149. messages verbatim to subprocessors.
  17150. The following message transfers temp_setp=20 to EEPROM, sends delay=10 to the
  17151. symbol table, requests the present value of temp, and executes the function
  17152. pwr_sav:
  17153. temp_setp>20 delay:10 temp? pwr_sav!
  17154. The response might be
  17155. temp:22 pwr_sav#16
  17156. The error message indicates that pwr_sav was not found in the symbol table of
  17157. the controller. (Such error messages are hopefully only encountered during
  17158. program development.) In this case the message indicates a discrepancy between
  17159. the PC and controller programs.
  17160.  
  17161.  
  17162. Message Interpretation
  17163.  
  17164.  
  17165. The interpreter parses the received messages into a task list which is handed
  17166. over to an execution function. The structure of the list records is:
  17167. typedef struct {
  17168. int command;
  17169. int type;
  17170. char *name;
  17171. int value; } toDoList;
  17172. command is a value specifying the command, type the type of the value (in this
  17173. case always INTEGER), *name a pointer to the name of the parameter, and value
  17174. the numerical value of the parameter.
  17175. In the controller the interpreter and reply assembler are integrated. In the
  17176. PC, however, parameters are sent through a message assembler and replies
  17177. decoded in a separate interpreter. The receiver is interrupt driven and
  17178. executes the interpreter after a full message has arrived. The interpreter
  17179. places the received values into the symbol table and transfers error messages
  17180. to the user interface. The PC interpreter resembles the controller
  17181. interpreter, which I am about to describe.
  17182.  
  17183.  
  17184. A Sample Application
  17185.  
  17186.  
  17187. This slightly modified version of the controller's interpreter and message
  17188. assembler will illustrate message handling and symbol table operations. (This
  17189. version runs on a PC.) Listing 1 presents the definitions and function
  17190. prototypes; Listing 2 shows the main program and message interpreter.
  17191. The main program reads a message from the keyboard. It parses the message,
  17192. then prints and executes the task list toDo. The task list is a list of
  17193. structures with one entry for each subcommand. The Parse function assumes the
  17194. message is formated as defined in Table 1. Since the grammar is quite simple,
  17195. there is no need for recursive descent or other strategies used in compilers.
  17196. There are several tests that check adherence to the format, and issue error
  17197. messages if discrepancies are found. (This is most valuable during program
  17198. development.) Finding an error stops the message interpretation. Inserting an
  17199. EMPTY command terminates the list.
  17200. GetNextToken reads the next token in the message string. If the first
  17201. character encountered is a letter, the token is a name. If it is a digit, the
  17202. token is a value. If the first character is neither a name nor a value, the
  17203. token is an operator. The function then copies the token into the token string
  17204. of the parser and sets the type parameter according to the type. The size of
  17205. the token-string (MAX_TOKNE_LEN) must be larger than the longest token that
  17206. can occur. The type parameter is set equal to the operator's defined constant.
  17207. Parser calls StroreToken to save the encountered names in a string pool. This
  17208. pool is recycled for each message by setting pPool=stringPool. The pool size
  17209. (STRINGPOOL) must be large enough to store all the names can occur in one
  17210. message. The address of the names is stored in the name element of the
  17211. structure.
  17212. The task list ToDo can now be processed by calling DoCommands. This functin is
  17213. essentially a loop which executes the commands through a switch statement.
  17214. However, it first initializes a pointer to the beginning of the txBuffer,
  17215. where the response message is stored. This buffer might be local to the more
  17216. hardware-dependent parts of the program and not globally accessible, so this
  17217. requires a function call to obtain the pointer. All function called in the
  17218. DoCommands maintain the buffer pointer by accepting it as an argument and
  17219. returning the possibly changed value.
  17220. SayError writes an error message to the transmit buffer. It receives a pointer
  17221. to the task list, where it finds the name relating to the error, and error
  17222. number.
  17223. The functions Assign, Request, ToEeprom, and RunCommand each begin by getting
  17224. a name from the task list and looking it up in the symbol table. If the name
  17225. isn't found, they write an error message to the transmit buffer.
  17226. The Assign function inserts the value in the symbol table. The Request
  17227. function reads the value from the symbol table and generates a response
  17228. submessage. ToEeprom puts the value into the symbol table and checks if the
  17229. parameter has a place in the EEPROM. If eeOffset is greater than or equal to
  17230. zero, it writes the value. If not, it issues an error message. RunCommand
  17231. executes the program pointed to in the symbol structure. If the pointer is
  17232. NULL, RunCommand generates an error message.
  17233. Assign, Request and ToEeprom all check for a function hook before returning.
  17234. If they find one they call it. Assign, Request, and ToEeprom also maintain the
  17235. transmitter buffer pointer so they can write to the buffer.
  17236. The program was compiled with Borland TurboC++ version 3.0. The library
  17237. functions are all UNIX and ANSI defined, so using other compilers should not
  17238. present any problem.
  17239.  
  17240.  
  17241. Final Thoughts
  17242.  
  17243.  
  17244. This symbolic data transfer scheme enabled us to implement a complete control
  17245. system from scratch in about one programmer-month. When we started we had no
  17246. clear picture of what the final result would be, so parameters were constantly
  17247. added and removed as we tried different control strategies. By using the
  17248. described method, we were able to respond quickly to these changes. This
  17249. arrangement also made it easier to add auxiliary parameters for debugging.
  17250. There are of course faster ways to transfer data, but the kinds of processes
  17251. maintained by embedded controllers are often slow enough that a minor increase
  17252. in response time isn't significant.
  17253. Figure 1 Information flow through the system
  17254. Table 1 Message formats and operators
  17255. <name> = <value> Assign value to parameter name
  17256. <name> ? Request the value of parameter name
  17257. <name> > <value> Transfer value to the EEPROM
  17258. <name> : <value> Current value of parameter name
  17259. <name> ! Execute name-command
  17260. <name> # <value> Error code value associated with name
  17261.  
  17262. Listing 1 Message interpreter definitions and function prototypes
  17263. /* parsdef.h */
  17264.  
  17265. /* No copyrights claimed */
  17266. #define ERROR 1
  17267. #define EMPTY 2
  17268. #define ASSIGN 3
  17269. #define REQUEST 4
  17270. #define TO_EEPROM 5
  17271. #define IS 6
  17272. #define COMMAND 7
  17273. #define INTEGER 8
  17274. #define NAME 10
  17275. #define NAME_ERROR 11
  17276. #define OP_ERROR 12
  17277. #define VAL_ERROR 13
  17278. #define END_ERROR 14
  17279. #define POOL_ERROR 15
  17280. #define UNDEF_SYMB 16
  17281. #define NOT_EEPROM 17
  17282. #define UNDEF_FUNC 18
  17283. #define MAX_TOKEN_LEN 10
  17284. #define MAX_STATEMENTS 20
  17285. #define STRINGPOOL 80
  17286. #define MESSAGELENGTH 80
  17287.  
  17288. typedef struct {
  17289. int command;
  17290. int type;
  17291. char *name;
  17292. int value;
  17293. } toDoList;
  17294.  
  17295. typedef struct {
  17296. char *name;
  17297. int ival;
  17298. char *(*func)(char *);
  17299. int eeOffset; /* -1 if not saved in eeprom */
  17300. } symTabEntry;
  17301.  
  17302. char *Assign(toDoList *pL, char *pTx);
  17303. void DoCommands(toDoList *pL);
  17304. symTabEntry *FindSymbol(char *name);
  17305. char *GetNextToken(char *begin, char *token, int *type);
  17306. void Parse(char *message, toDoList *pL);
  17307. void PrintToDo(toDoList *toDo);
  17308. char *Request(toDoList *pL, char *pTx);
  17309. char *Reset(char *ptr);
  17310. char *RunCommand(toDoList *pL, char *pTx);
  17311. char *SayError(toDoList *pL, int err, char *cp);
  17312. int StoreToken(char **strp, char *token);
  17313. int SymCmp(symTabEntry *a, symTabEntry *b);
  17314. char *ToEeprom(toDoList *pL, char *pTx);
  17315. char *TxBuffer(void);
  17316. void WriteEeprom(symTabEntry *pSym, int type);
  17317.  
  17318. /* End of File */
  17319.  
  17320.  
  17321. Listing 2 The message interpreter and assembler for the embedded controller,
  17322. implemented on a PC
  17323. /* pars.c */
  17324. /* no copyrights claimed */
  17325.  
  17326. #include <stdio.h>
  17327. #include <stdlib.h>
  17328. #include <ctype.h>
  17329. #include <string.h>
  17330. #include "parsdef.h"
  17331.  
  17332. static toDoList toDo[MAX_STATEMENTS];
  17333. static char stringPool[STRINGPOOL];
  17334. static char *pPool, *poolEnd;
  17335. static char message[MESSAGELENGTH];
  17336. static char *messList[]= {
  17337. "EMPTY","error","empty","assign","request",
  17338. "to_eeprom","is","command","integer","float",
  17339. "name","name_error","op_error","val_error",
  17340. "end_error","pool_error"
  17341. };
  17342.  
  17343. main()
  17344. {
  17345. while(1) {
  17346. pPool= stringPool;
  17347. poolEnd= stringPool+STRINGPOOL-1;
  17348. printf("\nmessage: ");
  17349. gets(message);
  17350. if(message[0]=='.') break;
  17351. Parse(message, toDo);
  17352. PrintToDo(toDo);
  17353. DoCommands(toDo);
  17354. }
  17355. }
  17356.  
  17357. /* first, the parsing */
  17358.  
  17359. void Parse(char *message, toDoList *pToDo)
  17360. {
  17361. char *pC, token[MAX_TOKEN_LEN+1];
  17362. int type, i;
  17363. pC= message;
  17364. for(i=0; i<MAX_STATEMENTS; i++) {
  17365. (pToDo+1)->command= EMPTY; /* mark end */
  17366. if(*pC=='\0'){pToDo->command= EMPTY; return;}
  17367. /* get name */
  17368. pC= GetNextToken(pC, token, &type);
  17369. if(StoreToken(&pToDo->name, token) != 0)
  17370. { pToDo->command=POOL_ERROR; return; }
  17371. if((type==ERROR)(type!=NAME))
  17372. { pToDo->command= NAME_ERROR; return;}
  17373. if(*pC=='\0') {pToDo->command=EMPTY; return;}
  17374. /* get operator */
  17375. pC= GetNextToken(pC, token, &type);
  17376. if(type==ERROR)
  17377. {pToDo->command=OP_ERROR; return;}
  17378. pToDo->command= type;
  17379. /* get value */
  17380. if((type==ASSIGN)(type==TO_EEPROM)
  17381. (type==IS)) {
  17382. if(*pC=='\0')
  17383. {pToDo->command=VAL_ERROR; return;}
  17384. pC= GetNextToken(pC, token, &type);
  17385.  
  17386. pToDo->type= type;
  17387. if(type==INTEGER)
  17388. pToDo->value= atoi(token);
  17389. else {pToDo->command= VAL_ERROR; return;}
  17390. }
  17391. else pToDo->type= INTEGER;
  17392. if(pToDo->command== EMPTY) return;
  17393. pToDo++;
  17394. }
  17395. pToDo->command= END_ERROR;
  17396. return;
  17397. }
  17398.  
  17399. char *GetNextToken(char *begin, char *token, int type)
  17400. {
  17401. char *pC, *pT;
  17402. int i;
  17403. pC= begin;
  17404. pT= token;
  17405. *token='\0';
  17406. *type= EMPTY;
  17407. if(*pC == '\0') {return pC;}
  17408. /* remove leading spaces */
  17409. while(*pC=='') pC++;
  17410. if(*pC=='\0') return pC;
  17411. if(isalpha(*pC)) { /* name */
  17412. *type= NAME;
  17413. *pT++= *pC++;
  17414. for(i=1; i<MAX_TOKEN_LEN; i++) {
  17415. if((isalpha(*pC)) (isdigit(*pC))
  17416. (*pC=='_')) *pT++= *pC++;
  17417. else {
  17418. *pT= '\0';
  17419. return pC;
  17420. }
  17421. }
  17422. *type= ERROR;
  17423. return pC;
  17424. }
  17425. else if((isdigit(*pC))(*pC=='+'))
  17426. (*pC=='-')) { /* number */
  17427. *pT++= *pC++;
  17428. for(i=1; i<MAX_TOKEN_LEN; i++) {
  17429. if(isdigit(*pC)) *pT++= *pC++;
  17430. else {
  17431. *pT= '\0';
  17432. *type= INTEGER;
  17433. return pC;
  17434. }
  17435. }
  17436. *type= ERROR;
  17437. return pC;
  17438. }
  17439. else if(*pC== '=') *type= ASSIGN;
  17440. else if(*pC== '?') *type= REQUEST;
  17441. else if(*pC== '>') *type= TO_EEPROM;
  17442. else if(*pC== ':') *type= IS;
  17443. else if(*pC== '!') *type= COMMAND;
  17444. else {*type= ERROR; return ++pC;}
  17445.  
  17446. return ++pC;
  17447. }
  17448.  
  17449. int StoreToken(char **ppName, char *token)
  17450. {
  17451. int length;
  17452. *ppName= pPool;
  17453. length= strlen(token)+1;
  17454. if((pPool+length)>=poolEnd) return -1;
  17455. strcpy(pPool, token);
  17456. pPool+= length;
  17457. return 0;
  17458. }
  17459.  
  17460. /* and now, the action */
  17461.  
  17462. static symTabEntry symTab[]= {
  17463.  
  17464. {"reset" , 0 , Reset, -1},
  17465. {"status" , 35 , NULL , -1},
  17466. {"temp" , 16 , NULL , 20}
  17467. };
  17468.  
  17469. void PrintToDo(toDoList *toDo)
  17470. {
  17471. while(toDo->command != EMPTY) {
  17472. printf("\n %s %s '%s'",
  17473. messList[toDo->command],
  17474. messList[toDo->type], toDo->name);
  17475. if(toDo->type==INTEGER)
  17476. printf(" %d", toDo->value);
  17477. toDo++;
  17478. }
  17479. }
  17480.  
  17481. int SymCmp(symTabEntry *a, symTabEntry *b)
  17482. {
  17483. return strcmp(a->name, b->name);
  17484. }
  17485.  
  17486. symTabEntry *FindSymbol(char *pName)
  17487. {
  17488. symTabEntry dummy, *p;
  17489. dummy.name= pName;
  17490. p= (symTabEntry*) bsearch(&dummy, symTab,
  17491. sizeof(symTab)/sizeof(symTabEntry),
  17492. sizeof(symTabEntry),
  17493. (int(*)(const void*, const void*))SymCmp);
  17494. return p;
  17495. }
  17496.  
  17497. char *Reset(char *pC)
  17498. {
  17499. printf("\nExecuting 'Reset'");
  17500. return pC;
  17501. }
  17502.  
  17503. void DoCommands(toDoList *pToDo)
  17504. {
  17505.  
  17506. char *txBuffer, *pTx;
  17507. txBuffer= pTx= TxBuffer();
  17508. *pTx= '\0';
  17509. while(pToDo->command != EMPTY) {
  17510. switch(pToDo->command) {
  17511. case ERROR:
  17512. case NAME_ERROR:
  17513. case OP_ERROR:
  17514. case VAL_ERROR:
  17515. case END_ERROR:
  17516. case POOL_ERROR:
  17517. pTx= SayError(pToDo, pToDo->command, pTx);
  17518. break;
  17519. case ASSIGN: pTx= Assign(pToDo, pTx);
  17520. break;
  17521. case REQUEST: pTx= Request(pToDo, pTx);
  17522. break;
  17523. case TO_EEPROM: pTx= ToEeprom(pToDo, pTx);
  17524. break;
  17525. case COMMAND: pTx= RunCommand(pToDo, pTx);
  17526. break;
  17527. default: break;
  17528. }
  17529. ++pToDo;
  17530. }
  17531. printf("\ntxBuffer: '%s'\n", txBuffer);
  17532. }
  17533.  
  17534. char *SayError(toDoList *pToDo, int err, char *pC)
  17535. {
  17536. char str[MAX_TOKEN_LEN+1], *pS;
  17537. pS= pToDo->name;
  17538. while(*pC++=*pS++);
  17539. pC--;
  17540. *pC++='#';
  17541. sprintf(str,"%-d", err);
  17542. pS= str;
  17543. while(*pC++= *pS++);
  17544. return --pC;
  17545. }
  17546.  
  17547. char *Assign(toDoList *pToDo, char *pTx)
  17548. {
  17549. symTabEntry *pSym;
  17550. pSym= FindSymbol(pToDo->name);
  17551. if(pSym==NULL) {
  17552. pTx= SayError(pToDo, UNDEF_SYMB, pTx);
  17553. return pTx;
  17554. }
  17555. if(pToDo->type==INTEGER) pSym->ival= pToDo->value;
  17556. if(pSym->func!=NULL) pTx= (*pSym->func)(pTx);
  17557. return pTx;
  17558. }
  17559.  
  17560. char *Request(toDoList *pToDo, char *pTx)
  17561. {
  17562.  
  17563. symTabEntry *pSym;
  17564. char *pS, str[MAX_TOKEN_LEN+1];
  17565.  
  17566. pSym= FindSymbol(pToDo->name);
  17567. if(pSym==NULL) {
  17568. pTx= SayError(pToDo, UNDEF_SYMB, pTx);
  17569. return pTx;
  17570. }
  17571. pS= pToDo->name;
  17572. while(*pTx++=*pS++);
  17573. pTx--;
  17574. *pTx++= '=';
  17575. if(pToDo->type==INTEGER)
  17576. sprintf(str,"%-d", pSym->ival);
  17577. pS= str;
  17578. while(*pTx++=*pS++);
  17579. if(pSym->func!=NULL) pTx= (*pSym->func)(pTx);
  17580. return --pTx;
  17581. }
  17582.  
  17583. char *ToEeprom(toDoList *pToDo, char *pTx)
  17584. {
  17585. symTabEntry *pSym;
  17586. pSym= FindSymbol(pToDo->name);
  17587. if(pSym==NULL) {
  17588. pTx= SayError(pToDo, UNDEF_SYMB, pTx);
  17589. return pTx;
  17590. }
  17591. if(pSym->eeOffset<O) {
  17592. pTx= SayError(pToDo, NOT_EEPROM, pTx);
  17593. return pTx;
  17594. }
  17595. if(pToDo->type==INTEGER) {
  17596. pSym->ival= pToDo->value;
  17597. WriteEeprom(pSym, INTEGER];
  17598. }
  17599. if(pSym->func!=NULL) pTx= (*pSym->func)(pTx);
  17600. return pTx;
  17601. }
  17602.  
  17603. char *RunCommand(toDoList *pToDo, char *pTx)
  17604. {
  17605. symTabEntry *pSym;
  17606. pSym= FindSymbol(pToDo->name);
  17607. if(pSym==NULL) {
  17608. pTx= SayError(pToDo, UNDEF_SYMB, pTx);
  17609. return pTx;
  17610. }
  17611. if(pSym->func!=NULL) pTx= (*pSym->func)(pTx);
  17612. else {
  17613. pTx=SayError(pToDo, UNDEF_FUNC, pTx);
  17614. return pTx;
  17615. }
  17616. return pTx;
  17617. }
  17618.  
  17619. char *TxBuffer(void)
  17620. {
  17621. static char buffer[200];
  17622. return buffer;
  17623. }
  17624.  
  17625.  
  17626. void WriteEeprom(symTabEntry *pSym, int type)
  17627. {
  17628. if(type==INTEGER) printf(
  17629. "\nTo EE, type: %d off: %d val: %d\n",
  17630. type, pSym->eeOffset, pSym->ival);
  17631. return;
  17632. }
  17633.  
  17634. /* End of File */
  17635.  
  17636.  
  17637.  
  17638.  
  17639.  
  17640.  
  17641.  
  17642.  
  17643.  
  17644.  
  17645.  
  17646.  
  17647.  
  17648.  
  17649.  
  17650.  
  17651.  
  17652.  
  17653.  
  17654.  
  17655.  
  17656.  
  17657.  
  17658.  
  17659.  
  17660.  
  17661.  
  17662.  
  17663.  
  17664.  
  17665.  
  17666.  
  17667.  
  17668.  
  17669.  
  17670.  
  17671.  
  17672.  
  17673.  
  17674.  
  17675.  
  17676.  
  17677.  
  17678.  
  17679.  
  17680.  
  17681.  
  17682.  
  17683.  
  17684.  
  17685.  
  17686.  
  17687.  
  17688.  
  17689. ROMLDR, an Embedded System Program Locator
  17690.  
  17691.  
  17692. Charles B. Allison
  17693.  
  17694.  
  17695. Charles Allison has been working with microprocessor hardware and firmware in
  17696. embedded systems since 1976. He has a Bachelor of Science degree in physics
  17697. and a Master of Business Administration degree. Charles has a microprocessor
  17698. consulting business, Allison Technical Services, where he has been developing
  17699. embedded control and monitoring products for clients since 1984. Charles can
  17700. be reached through CompuServe 71005,1502, his BBS/FAX line at (713)-777-4746
  17701. or his company, ATS, 8343 Carvel, Houston, TX 77036.
  17702.  
  17703.  
  17704. MS-DOS software development tools such as C compilers and debuggers have
  17705. become marvelously sophisticated and useful. They can also provide a
  17706. cost-effective means for developing embedded systems. The purpose of this
  17707. article is to introduce one of the aspects of using a high performance
  17708. DOS-based C compiler for embedded systems, that of relocating code and data
  17709. segments. I provide a program, ROMLDR, which can modify a program from the
  17710. MS-DOS EXE format to a located binary file format.
  17711. ROMLDR's principle purpose is to adapt DOS-based C compiler output for use in
  17712. EPROM-based embedded systems. Other uses include BIOS extensions, EPROM-based
  17713. MS-DOS applications, and relocation of MS-DOS programs in PC memory, such as
  17714. above the 640k MS-DOS memory limit. ROMLDR can also be used with EXE files
  17715. generated by languages other than C.
  17716. ROMLDR was written using Borland C 3.1. It should be possible to use either
  17717. Borland Turbo C or Microsoft C to make the ROMLDR program and to use for
  17718. embedded programs. I have tested ROMLDR only with the Borland C 3.1 compiler.
  17719.  
  17720.  
  17721. Embedding DOS-Compiled Programs
  17722.  
  17723.  
  17724. To use DOS-compiled programs in EPROM-based embedded systems, you must provide
  17725. several additional components, as well as resolve some unique programming
  17726. issues. A number of the required components, such as the startup code, depend
  17727. on the compiler, target hardware, and application. (See the sidebar "Coding
  17728. for Embedded Applications" for a brief discussion on embedded code
  17729. requirements.)
  17730. Once you have attended to all these details, you can program, compile, and
  17731. link the various program files into an MS-DOS EXE file. However, you can't
  17732. just burn your EXE file into EPROM and go. You must explicitly perform a step
  17733. that MS-DOS performs implicitly when it loads an EXE file.
  17734. MS-DOS modifies programs with the EXE extent when it loads them into memory.
  17735. This modification, often referred to as a fix up (also known as the locate
  17736. function), consists of modifying the program's segment values for the actual
  17737. memory address where it is to run. By performing fix ups, DOS can load an
  17738. executable almost anywhere in real-mode memory. (DOS can load small programs
  17739. with the COM extent anywhere and run them without modification.)
  17740. DOS performs fix ups by adding the file's load-address segment value to all
  17741. segment addresses stored in the code and data areas of the program.
  17742. Unlike DOS programs, most embedded systems programs reside in and execute from
  17743. EPROM. Embedded system locators must perform fix ups prior to placing the
  17744. program in EPROM and must take into account that variables will be located in
  17745. RAM, through their initialized values for startup are still in EPROM.
  17746.  
  17747.  
  17748. Locating for Embedded Systems
  17749.  
  17750.  
  17751. Before I present my program in detail, I want to outline the major parts of
  17752. the location process. I will first describe the structure of a DOS EXE file,
  17753. and how that structure is reflected in my code. Next, I will describe the
  17754. location process.
  17755.  
  17756.  
  17757. EXE File Format
  17758.  
  17759.  
  17760. The EXE file consists of several sections, including a header, program code,
  17761. data initialization values, and optional program debug information. Refer to
  17762. structure EXE_HDR at the beginning of Listing 1 for the layout and definitions
  17763. of the various parameters. The header section consists of several parameters
  17764. which define the size of the file and the size of the header. Following these
  17765. parameters is a section of fix-up far pointers.
  17766. Each of these pointers targets a location in the program code containing a
  17767. segment address value that must be modified with the correct load address.
  17768. These pointers are stored in standard 80x86 segment:offset format relative to
  17769. the beginning of the code section. The pointers' segment values are derived
  17770. from teh MAP file segment table by using the top four hexadecimal digits from
  17771. the beginning address listed for each segment. (MAP files are generated by the
  17772. compiler. MAP file segments consist of the top four hexadecimal digits of the
  17773. beginning address.)
  17774. There are num_reloc fix-up pointers in the header section. These pointers
  17775. begin at the offset off_reloc from the beginning of the header. (Note that
  17776. fix-up pointers may not be sorted by address as they occur in the EXE file.)
  17777. Following the header is the program's code section. This section consists of
  17778. one or more separate segments, the number and type of which depend on the
  17779. program and its memory model. Following the code is the initialized data
  17780. section. Then comes the uninitialized data section, and finally the stack.
  17781.  
  17782.  
  17783. The Location Process
  17784.  
  17785.  
  17786. Once it has loaded a program into memory, the MS-DOS loader adds the code
  17787. section's segment address to the segment value stored in each location
  17788. requiring a fix up. The loader finds these locations by dereferencing each
  17789. fix-up pointer in the header. MS-DOS sets the CPU's stack registers to
  17790. disp_stack_seg:sp and calls the program at address rel_cs_seg:ip. (Note: For
  17791. the sake of illustration, I use disp_stack_seg, sp, rel_cs_seg, and ip to
  17792. represent values stored at specific offsets within the EXE header. By
  17793. referring to fields of the same name in my struct, EXE_HDR, you can see where
  17794. these values are stored in the header.) MS-DOS also provides some environment
  17795. and header information to the loaded program through register contents.
  17796. Most, but not quite all of the information necessary to generate rommable
  17797. absolute binary files already exists in the EXE file. The rest of the
  17798. information must come from the segment data in the compiler's MAP file and
  17799. from configuration information provided by the user in a loader configuration
  17800. file.
  17801.  
  17802.  
  17803. Program Description
  17804.  
  17805.  
  17806. ROMLDR uses the linked EXE file and its MAP file to create a binary file that
  17807. can be programmed into EPROMs.
  17808. Main begins by allocating a far buffer to contain program segments. This
  17809. buffer should be 0xl0000 bytes in length to ensure that any size code segment
  17810. can be processed. (While testing from within the Borland IDE, I had to reduce
  17811. the buffer's size significantly due to memory constraints.) A simple error
  17812. routine, term_error, generates error messages for a variety of potential
  17813. problems, and provides for program termination.
  17814. After allocating a buffer, ROMLDR reads the configuration file (CFG) specified
  17815. on the command line. This file contains the names for the EXE, MAP, and BIN
  17816. output files, EPROM and RAM hexadecimal load addresses, and the class name of
  17817. the first RAM segment. Table 1 shows the CFG file format. CFG file parameters
  17818. must be located on separate lines and separated by spaces. On each line,
  17819. ROMLDR ignores any characters occurring after the list of required parameters.
  17820.  
  17821.  
  17822. Reading the MAP File
  17823.  
  17824.  
  17825. ROMLDR executes a while loop to read and process lines of text from the MAP
  17826. file. (The MAP file used with ROMLDR should be the short version, which
  17827. contains only the segment table.) ROMLDR extracts memory allocation class
  17828. names, plus their starting and ending addresses, and stores them in an array
  17829. of structures called maptable. ROMLDR performs a simple length check using
  17830. configuration variable class_loc to determine if the current line contains
  17831. segment information. ROMLDR expects the line to be in a fixed column format,
  17832. with the class name occurring at offset class_loc. ROMLDR converts address
  17833. values to long integers, and compares class names with ram_class, a
  17834. configuration variable used to define the beginning of RAM. The beginning
  17835. segment address is stored in ramdata. ROMLDR currently will process a maximum
  17836. of 120 segments.
  17837.  
  17838.  
  17839.  
  17840. Processing the Header
  17841.  
  17842.  
  17843. Once ROMLDR has acquired the MAP file, it reads the EXE header portion via
  17844. function gethdr. This function first reads in 32 bytes of the header to
  17845. determine the header's size and then loads the rest of the header. This
  17846. function stores header information in an array named header which can contain
  17847. a maximum of 10,000 bytes.
  17848. ROMLDR then sorts the fix-up pointers into ascending address order. Function
  17849. sort_table uses qsort to sort the pointers. Function cmp_ptr, supplied as an
  17850. argument to qsort, compares values for qsort by converting pointers from
  17851. segment:offset to long integer form.
  17852. After sorting the pointers, ROMLDR performs fix ups on each segment, using a
  17853. for loop to iterate through all segments. Function read_segm reads each
  17854. segment from the EXE file and returns the segment size. If the segment length
  17855. is non-zero, ROMLDR calls function fix_segm to run through any fix ups needed
  17856. for the segment and then calls write_segm to output the processed segment to
  17857. the BIN file. (A logical enhancement to ROMLDR would be to output the segments
  17858. in a standard hex format, such as Intel hex format.)
  17859.  
  17860.  
  17861. HOW ROMLDR Handles EPROM and RAM
  17862.  
  17863.  
  17864. EPROM segments require different modifications than RAM segments. ROMLDR
  17865. treats all segments at or below the class named ENDCODE as EPROM and treats
  17866. those above ENDCODE as RAM. The location process currently terminates on
  17867. reaching the last segment, the STACK class. (The BIN file, however, needs only
  17868. to contain code and data segments up to the last initialized data value.)
  17869. The ENDCODE class name is special for another reason. ROM based systems
  17870. typically must transfer initialized data from EPROM to RAM. Therefore, ROMLDR
  17871. will modify all references to data segments to refer to the RAM locations and
  17872. not to the initial values located in the EPROM. (The location for the initial
  17873. values must be used in the startup code so that they can be transferred to
  17874. RAM.) Segment ENDCODE is used for this purpose. I make the ENDCODE segment's
  17875. length less than 16 bytes, locate it on a paragraph boundary, and ensure that
  17876. the beginning data segment is also aligned by paragraph. As a result, the
  17877. beginning ROM location for initialized data becomes ENDCODE+1. Since ENDCODE's
  17878. address is less than the beginning of the RAM segment, it will refer to the
  17879. ROM address just below the initialized data values.
  17880. ROMLDR modifies EPROM data by adding the configuration file's EPROM segment
  17881. address, stored in romsadr, to the code's existing segment value. (A more
  17882. sophisticated version of the program could offer the option of several
  17883. user-defined addresses and the names of the classes that would reside in
  17884. each.)
  17885. ROMLDR modifies RAM segments by first subtracting out the value of the first
  17886. RAM segment and then adding the configuration file's RAM segment location
  17887. value. This method allows the RAM locations to begin at the
  17888. configuration-defined starting value. The subtraction was not necessary for
  17889. code segments since they began with a zero segment value.
  17890.  
  17891.  
  17892. Example Code
  17893.  
  17894.  
  17895. Listing 2 is DEMO.C, a typical "Hello World" program with some added items to
  17896. provide examples of values for several segment classes. Listing 3, DEMO.MAP,
  17897. is the map file generated for DEMO.C using the example startup code in Listing
  17898. 4 instead of the standard Borland startup code. Note that the _INIT_ segment
  17899. contains some values which are addresses of library initialization routines
  17900. that should be called in order of priority. The example startup code does not
  17901. yet include this section or the interrupt vector initialization section.
  17902. Examples of these can be found in your compiler's startup code. The sidebar
  17903. "Startup Code for Embedded Systems" provides a discussion of startup code
  17904. requirements.
  17905.  
  17906.  
  17907. ROMLDR Versus Commercial Products
  17908.  
  17909.  
  17910. ROMLDR has several shortcomings when compared to commercial locator packages.
  17911. When you use ROMLDR you must provide startup code for your application;
  17912. commercial products usually provide the basic startup code required as well as
  17913. code solutions for a variety of problems which must be overcome in various
  17914. embedded configurations. ROMLDR does not provide debugging support, but
  17915. commercial products usually provide some capabilities for the debugging of
  17916. application programs. Finally, vendors of commercial locator packages often
  17917. provide technical support; when you use a non-commercial package such as
  17918. ROMLDR you must solve all problems on your own.
  17919.  
  17920.  
  17921. Conclusions
  17922.  
  17923.  
  17924. While ROMLDR is intended primarily as a learning tool and an introduction to
  17925. embedded systems, it can prove useful for some low-end applications. ROMLDR
  17926. should be able to handle straightforward applications where there is one EPROM
  17927. and one RAM memory space. It can easily be modified for more complex
  17928. configurations, especially those which have specific fixed requirements.
  17929. Embedded systems often monitor and control equipment other than normal
  17930. computer peripherals. Embedded systems programmers must be extra cautious,
  17931. since bugs in their programs can place property and lives at risk. In this
  17932. situation, there is no substitute for understanding both the application and
  17933. the tools. Understanding how ROMLDR works may give you insight into how more
  17934. complex systems operate.
  17935. Coding for Embedded Applications
  17936. Writing embedded systems applications requires special efforts on the part of
  17937. the programmer, because of how these applications differ from non-embedded
  17938. applications. First, an embedded program that crashes can cause damage or
  17939. injury, while a non-embedded program (e.g. a word processor) may cause only a
  17940. certain amount of user frustration. Embedded systems must handle the
  17941. conceivable problems in stride and effectively recover from the inconceivable
  17942. without help from users.
  17943. Second, there are several special aspects to embedded code. Many embedded
  17944. systems run standalone programs without the support of complete operating
  17945. systems such as MS-DOS, so these programs must take control of relevant
  17946. interrupt vectors (including error conditions such as divide by 0 and the
  17947. Non-Maskable Interrupt). For these standalone programs, both the hardware and
  17948. the program may require setup code. Programs compiled to run under MS-DOS
  17949. contain startup and termination code to accept control from and return control
  17950. to the operating system. Standard library functions that don't access MS-DOS
  17951. under normal conditions may contain error-handling code that does access
  17952. MS-DOS or that would terminate the program in an unacceptable fashion within
  17953. an embedded system. In addition, some programs may attempt to access non-disk
  17954. MS-DOS functions, such as those used to modify interrupt vectors. If you port
  17955. such programs to an embedded system, you must provide code to perform
  17956. equivalent functions within the embedded system operating environment. An
  17957. embedded program must start (or restart) in a known state. Since the program
  17958. cannot obtain state information from a command line, it must find initial
  17959. state data in EPROM.
  17960. Startup Code for Embedded Systems
  17961. Startup code consists of code to setup and to terminate an application
  17962. program. Startup code is typically written in assembly language and the source
  17963. code is sometimes provided with the C compiler. Replacement libraries often
  17964. include replacement startup code that can be used when the normal C compiler
  17965. library functions are not used.
  17966. Startup code for EPROM-based systems must provide additional functionality
  17967. which depends on the specifics of the system.
  17968. Embedded system startup code usually performs the following steps:
  17969. 1. Establish order of segment classes.
  17970. 2. Set up the program stack.
  17971. 3. Transfer initialized values to RAM from EPROM.
  17972. 4. Zero uninitialized RAM values.
  17973. 5. Set up error interrupt vectors.
  17974. 6. Call necessary library initialization routines.
  17975. 7. Call the application.
  17976. The startup code must also provide the following components:
  17977. 8. Error shut-down code to terminate or restart.
  17978. 9. Exit shut-down code to terminate or restart.
  17979. 10. Any error and miscellaneous routines.
  17980. 11. Initialization of application before calling main function.
  17981. 12. A reset vector for standalone applications.
  17982. There are two approaches to creating a custom startup module. You can start
  17983. with the original library or compiler code and modify it to handle the added
  17984. embedded requirements, or you can start with a minimal embedded system startup
  17985. module and extend it as required.
  17986. Custom startup code may not provide all features described in a compiler's
  17987. documentation. Environment, argv and argc, and even common option variables
  17988. may not be implemented. For example, Borland C allows you to set stack size by
  17989. initializing a far variable, _stklen. This variable is created by the Borland
  17990. startup code and may be replaced in custom startup code by a simple stack-size
  17991. definition.
  17992. Startup code defines segments and segment classes and their order. While
  17993. compilers often allow these names to be changed for modules, the compiled
  17994. libraries are still expecting certain module names to exist. Warnings abound
  17995. over changing the basic segment order so extreme care should be used if that
  17996. appears to be necessary. The addition of segments and classes is much less
  17997. critical. As shown in Listing 4, you can even add them virtually to make the
  17998. MAP more descriptive.
  17999.  
  18000. Listing 4 is a simple example of startup code. I provide it as a template
  18001. rather than a complete example since some portions are compiler dependent. You
  18002. may still need to add several code sections for an application. Refer to your
  18003. compiler's startup code module for specific details.
  18004. Table 1 CFG file format
  18005. Mapfile Exefile Binfile // filenames with no path
  18006. ROM_Address RAM_address // 4 digit hex addresses [0...9,a...f]
  18007. Ram_Class // name for first RAM segment
  18008.  
  18009. Listing 1 The EXE locator program
  18010. /* ROMLDR.C EXE locator program
  18011. written by: Charles B. Allison
  18012. last change: 11-3-93 */
  18013.  
  18014. #include <sys\stat.h>
  18015. #include <io.h>
  18016. #include <dos.h>
  18017. #include <stdio.h>
  18018. #include <stdlib.h>
  18019. #include <alloc.h>
  18020. #include <conio.h>
  18021. #include <ctype.h>
  18022. #include <fcntl.h>
  18023. #include <string.h>
  18024. #define BC 1
  18025. /* BC is Borland c, else assume Microsoft */
  18026. typedef struct {
  18027. unsigned sig; /* signature = 4d5ah */
  18028. unsigned 1st_sec_lng;
  18029. /*length of last sector in file modulo 512*/
  18030. unsigned file_size;
  18031. /* size of file in 512 byte pages includes hdr*/
  18032. unsigned num_reloc;
  18033. /* number of relocation items */
  18034. unsigned hdr_siz;
  18035. /* # of 16 byte paragraphs in header */
  18036. unsigned min_ld_para;
  18037. /* min # of paragraphs above load file */
  18038. unsigned max_ld_para;
  18039. /* max # of paragraphs requested by file */
  18040. unsigned disp_stack_seg;
  18041. /* rel displacement of stack segment */
  18042. unsigned sp;
  18043. /* contents of stack ptr on entry to prog */
  18044. unsigned chksm; /* check sum for file */
  18045. unsigned ip; /* beginning instruction ptr */
  18046. unsigned rel_cs_seg; /* relative cs segment */
  18047. unsigned off_reloc;
  18048. /* offset to 1st relocation item typ. 1e */
  18049. unsigned over_lay; /* overlay number */
  18050. unsigned rsrved; /* ?? reserved ?? */
  18051. /* relocation item format is seg:off location
  18052. relative to the beginning of the code section */
  18053. } EXE_HDR;
  18054. struct MP_TBL {
  18055. long addrs; /* segment beg. address */
  18056. long haddrs; /* segment high addrs*/
  18057. char class[12]; /* class of object */
  18058. } maptable[120];
  18059.  
  18060. void sort_table(void);
  18061. void gethdr(char *bf);
  18062.  
  18063. long hexcvt(char *num);
  18064. size_t read_block(char far *segbuf, size_t segsz);
  18065. long read_segm(int i);
  18066. size_t write_block(char far *segbuf,size_t segsz);
  18067. long write_segm(long segsize);
  18068. int fix_segm(int i);
  18069. int config(char *cfgfile);
  18070. void term_error(int numerr);
  18071.  
  18072. unsigned (*ch_ptr)[2];/* pointer to translation table
  18073. [0] = offset,[1] = segment*/
  18074. char mfile[14] = "rom.map"; /* dumy file names */
  18075. char bfile[14] = "rom.bin";
  18076. char efile[14] = "rom.exe";
  18077. /*columns for starting and ending addresses in MAP */
  18078. #define LCOL 1
  18079. #define HCOL 8
  18080. #ifdef BC
  18081. #define MAPCOL 41;
  18082.  
  18083. /*bc map file class column*/
  18084. #else
  18085. #define MAPCOL 45;
  18086. /*ms map file class column */
  18087. #endif
  18088. int class_loc = MAPCOL;
  18089. FILE *mapfile,*exefile,*binfile;
  18090. #define BUF_SIZE 60
  18091. char mapstring[BUF_SIZE];
  18092. long fsize; /*number of bytes in exe file */
  18093. int nsegs; /* number of segments in map */
  18094. unsigned romsadr = 0xf000,ramsadr=0x40;
  18095. char header[10000];
  18096. EXE_HDR *filhdr = (EXE_HDR *)header;
  18097. char far *seg_buffer;
  18098. int next_fix = 0;
  18099. char ram_class[15] = "FAR_DATA";
  18100. unsigned ramdata; /* beginning ram segment*/
  18101. /* ************ main ************* */
  18102. int main(int argc,char *argv[])
  18103. {
  18104. int r_class_flag=1,i;
  18105. long tmp,ssize;
  18106.  
  18107. if((seg_buffer = (char far *) farmalloc(0x10000L))
  18108. == NULL) term_error(0);
  18109. if(argc == 1) term_error(-1); /*any cfg file name? */
  18110. config(argv[1]);
  18111. i=0;
  18112. while (fgets(mapstring,BUF_SIZE,mapfile))
  18113. {
  18114. /* process map file from mapstring input */
  18115. /* maptable[1] - n contains the segments -
  18116. class STACK should be last one */
  18117. /* ends with i having n+1 segments */
  18118. if( (int)strlen(mapstring) > class_loc+1)
  18119. { /* get rid of \n at end of string */
  18120. mapstring[(int)strlen(mapstring)-1]='\0';
  18121. if((tmp = hexcvt(&mapstring[LCOL])) >= 0)
  18122.  
  18123. {
  18124. maptable[i].addrs = tmp;
  18125. maptable[i].haddrs=hexcvt(&mapstring[HCOL]);
  18126. &mapstring[class_loc]);
  18127. strcpy(maptabte[i].class,
  18128. &mapstring[class_loc]);
  18129. if(r_class_flag)
  18130. if(strcmp(&mapstring[class_loc],ram_class)==0)
  18131. { /*set it to first class occurance*/
  18132. ramdata = (maptable[i].addrs) >>4;
  18133. r_class_flag = 0;
  18134. }
  18135. printf("\n Segment %4.41x Class %s",
  18136. maptable[i].addrs/16,maptable[i].class);
  18137. i++;
  18138. }/* end - if hexcvt */
  18139. } /* end if strlen */
  18140. if(i>=119) break; /* error too many segments */
  18141. } /* end while */
  18142. if(feof(mapfile))
  18143. printf("\nend of file\n");
  18144. else
  18145. printf("\nerror reading map file\n");
  18146. nsegs = i-1; /* number of segments [1 to nsegs] */
  18147. gethdr(header); /* read in the exe header info */
  18148. /* size of object section of file */
  18149. fsize = (long) ((512L * (filhdr->file_size-1)) +
  18150. filhdr->lst_sec_lng - 16L * filhdr->hdr_siz);
  18151. printf("\nStartup Address %4.4x:%4.4x\n",romsadr+
  18152. filhdr->rel_cs_seg,filhdr->ip);
  18153. printf("Rom Size %lx\n",fsize);
  18154. /* process the exe file header - sort fix ups */
  18155. sort_table();
  18156. /* read in exe file by segment
  18157. do fixups from map table and
  18158.  
  18159. write it to output file */
  18160. for(i=0;i < nsegs;i++)
  18161. {
  18162. if((ssize = read_segm(i)) > 0L )
  18163. { /* ignore 0 length segments */
  18164. fix_segm(i);
  18165. write_segm(ssize );
  18166. }
  18167. }
  18168. /* done - end the program */
  18169. farfree(seg_buffer);
  18170. fcloseall();
  18171. return 0;
  18172. }
  18173. /* ****************** */
  18174. /* qsort routine for far pointers */
  18175. int cmp_ptr(const void *a, const void *b)
  18176. {
  18177. long vala,valb;
  18178. vala=((long)((unsigned *)a)[0])+
  18179. (((unsigned *)a)[1]<<4);
  18180. valb=((long)((unsigned *)b)[0])+
  18181. (((unsigned *)b)[1]<<4);
  18182.  
  18183. vala -= valb;
  18184. if(vala < 0) return -1;
  18185. if(vala > 0) return 1;
  18186. return 0;
  18187. }
  18188.  
  18189. /*------------ sort_table ----------------*/
  18190. /* sort header table */
  18191. void sort_table(void)
  18192. {
  18193. qsort((void *)&header[filhdr->off_reloc],
  18194. filhdr->num_reloc,4,cmp_ptr);
  18195. }
  18196. /* -------------- read_block ---------------- */
  18197. size_t read_block(char far *segbuf,size_t segsz)
  18198. {
  18199. if(fread(segbuf,1,segsz,exefile) != segsz)
  18200. term_error(-7);
  18201. return segsz;
  18202. }
  18203. /* -------------- read_segm.------------------ */
  18204. long read_segm(int i)
  18205. {
  18206. long segsize;
  18207. segsize = maptable[i].haddrs - maptable[i].addrs;
  18208. if(!segsize) return 0;
  18209. segsize += maptable[i+1].addrs-(maptable[i].haddrs);
  18210. if(segsize <= 0x8000)
  18211. { read_block(seg_buffer,(size_t)segsize);
  18212. } else {
  18213. read_block(seg_buffer,0x8000);
  18214. read_block((&seg_buffer[0x8000]),
  18215. (size_t)(segsize-0x8000));
  18216. }
  18217. return segsize;
  18218. }
  18219. /* ------------------ write_block -------------------- */
  18220. size_t write_block(char far *segbuf,size_t segsz)
  18221. {
  18222. if(fwrite(segbuf,1,segsz,binfile) != segsz)
  18223. term_error(-8);
  18224. return segsz;
  18225. }
  18226. /* ---------------- write_segm ------------------ */
  18227. long write_segm(long segsize)
  18228. {
  18229. if(!segsize) return 0;
  18230. if(segsize <= 0x8000)
  18231. { write_block(seg_buffer,segsize);
  18232. } else {
  18233. write_block(seg_buffer,0x8000);
  18234. write_block((&seg_buffer[0x8000]),
  18235. (size_t)(segsize-0x8000));
  18236. }
  18237. return segsize;
  18238. }
  18239. /* ------------ fix_segm -------------- */
  18240. int fix_segm(int i)
  18241. {
  18242.  
  18243. unsigned tmp,cseg,fixup;
  18244. unsigned far * fixptr;
  18245. cseg = (unsigned)(maptable[i].addrs/16L);
  18246. while(next_fix < filhdr->num_reloc)
  18247. {
  18248. if(ch_ptr[next_fix][1] > cseg) break;
  18249. tmp = ch_ptr[next_fix][0]; /*offset into buffer*/
  18250. fixptr = (unsigned far *) &seg_buffer[tmp];
  18251. fixup = *fixptr;
  18252. /* modify segment fixup according to type */
  18253. if(fixup >= ramdata)
  18254. {/* modify for ram */
  18255. fixup -= ramdata;
  18256. fixup += ramsadr;
  18257. }
  18258. else
  18259. {/* handle as rom */
  18260. fixup += romsadr;
  18261. }
  18262. *fixptr = fixup;
  18263. next_fix++;
  18264. } /*end while */
  18265. return 0 ;
  18266. }
  18267. /* ------------ hexcvt -------------- */
  18268. /* do hex digits to unsigned long */
  18269. long hexcvt(char *num)
  18270. {
  18271. char *term;
  18272. long value;
  18273. value = strtoul(num,&term,16);
  18274. return value;
  18275. }
  18276. /* -------------- gethdr ---------------- */
  18277. void gethdr(char *buf )
  18278. {
  18279. int i,j=32; /*index counter */
  18280. if(fread(&buf[0],l,32,exefile) < 32)
  18281. term_error(-6);
  18282. /* have filhdr contents so get size of full header */
  18283. if(fread(&buf[j],16,filhdr->hdr_siz-2,exefile)
  18284. < filhdr->hdr_siz-2) term_error(-6);
  18285. (unsigned *)ch_ptr =
  18286. (unsigned *)(&buf[filhdr->off_reloc]);
  18287. /* get address of relocation table - ch_ptr [n][m]
  18288. m - 0 offset, 1 - seg, n relocation # */
  18289. }
  18290. /*---------------------- config.------------------------*/
  18291. /* get configuration data */
  18292. int config(char *cfgfile)
  18293. {
  18294. FILE *cfg;
  18295. char buf[80];
  18296. if((cfg = fopen(cfgfile,"r"))==NULL) term_error(-1);
  18297. if(fgets(buf,80,cfg) == NULL) term_error(-2);
  18298. if(sscanf(buf,"%s %s %s",&mfile,
  18299. &efile, &bfile) != 3) term_error(-2);
  18300. /* Now try to open input file 1 */
  18301. if((mapfile=fopen(mfile,"r"))==NULL) term_error(-3);
  18302.  
  18303. if((exefile=fopen(efile,"rb"))==NULL) term_error(-4);
  18304. if((binfile=fopen(bfile,"wb"))==NULL) term_error(-5);
  18305. if(fgets(buf,80,cfg) == NULL) term_error(-7);
  18306. if(sscanf(buf,"%4x %4x",&romsadr,
  18307. &ramsadr) != 2) term_error(-7);
  18308. if(fgets(buf,80,cfg) == NULL) term_error(-7);
  18309. if(sscanf(buf,"%s",&ram_class) != 1) term_error(-7);
  18310. return 0;
  18311. }
  18312. /* error handler */
  18313. char *errlist[10] = {
  18314. "Memory Allocation Error",
  18315. "No configuration file, USAGE:romldr cfgfile.cfg",
  18316. "Configuration file error - File names", //-2
  18317. "Map File open error", //-3
  18318. "Exe File open error", //-4
  18319. "Bin File open error", //-5
  18320. "Error reading header", //-6
  18321. "Error reading exe file", //-7
  18322. "Error writing bin file", //-8
  18323. " "
  18324. };
  18325. void term_error(int errnum)
  18326. {
  18327. errnum = abs(errnum);
  18328. if(errnum >= 9) exit(-1);
  18329. printf("%s\n",errlist[errnum]);
  18330. farfree(seg_buffer);
  18331. exit(errnum);
  18332. }
  18333.  
  18334. /* End of File */
  18335.  
  18336.  
  18337. Listing 2 Sample program that produces segment classes
  18338. DEMO.C
  18339. /* Test Program for Rom Loader */
  18340. /* Large model with some far data*/
  18341. #include <conio.h>
  18342. #include <dos.h>
  18343.  
  18344. char msg[15] = "Hello World\n\r";
  18345. int locint;
  18346. int directvideo=1; /*BC direct to hw*/
  18347. int far test;
  18348. int far test2=0x55aa;
  18349. int far * tptr = &test;
  18350.  
  18351. void main(void)
  18352. {
  18353. int far * ptr2 = &test;
  18354. test = 0x1111;
  18355. locint = test+l;
  18356. cputs(msg);
  18357. }
  18358. /* End of File */
  18359.  
  18360.  
  18361. Listing 3 Map file for sample program
  18362.  
  18363. DEMO.MAP
  18364. Start Stop Length Name Class
  18365.  
  18366. 00000H 00000H 00000H ROMSTART_BEG CODE
  18367. 00000H 00E8FH 00E90H _TEXT CODE
  18368. 00E90H 00EC9H 0003AH DEMO_TEXT CODE
  18369. 00ED0H 00ED5H 00006H ENDCODE ENDCODE
  18370. 00EE0H 00EE0H 00000H IDATA_BEG IDATA_BEG
  18371. 00EE0H 00EE0H 00000H _FARDATA FAR_DATA
  18372. 00EE0H 00EE3H 00004H DEMO5_DATA FAR_DATA
  18373. 00EF0H 00FB9H 000CAH _DATA DATA
  18374. 00FBAH 00FBBH 00002H _CVTSEG DATA
  18375. 00FBCH 00FC1H 00006H _SCNSEG DATA
  18376. 00FC2H 00FC2H 00000H CONST CONST
  18377. 00FC2H 00FC7H 00006H _INIT_ INITDATA
  18378. 00FC8H 00FC8H 00000H _INITEND_ INITDATA
  18379. 00FC8H 00FC8H 00000H _EXIT_ EXITDATA
  18380. 00FC8H 00FC8H 00000H _EXITEND_ EXITDATA
  18381. 00FD0H 00FD0H 00000H IDATA_END IDATA_END
  18382. 00FD0H 00FD0H 00000H UDATA_BEG UDATA_BEG
  18383. 00FD0H 00FD1H 00002H _BSS BSS
  18384. 00FD2H 00FD2H 00000H _BSSEND BSSEND
  18385. 00FE0H 02FDFH 02000H STACK STACK
  18386. 02FE0H 02FE0H 00000H UDATA_END UDATA_END
  18387.  
  18388. Program entry point at 0000:0000
  18389.  
  18390.  
  18391. Listing 4 An example of startup code
  18392. NAME ROMSTART_TEXT
  18393. ;
  18394. ; example startup code for
  18395. ; imbedded systems use
  18396. ;
  18397. ; segment classes
  18398. ; High addrs (rom)
  18399. ; 'BOOT'
  18400. ; ""
  18401. ; ""
  18402. ; 'CODE' _TEXT segment
  18403. ;
  18404. ; ram
  18405. ;
  18406. ; 'STACK' UDATA_END segment
  18407. ; 'BSS' IDATA_END and UDATA_BEG segments
  18408. ; 'CONST'
  18409. ; 'DATA' IDATA_BEG segment
  18410. ;
  18411. ; Rename object file output to c0x.obj
  18412. ; where x = S,C,M,L, or H
  18413. ; and locate in project subdirectory
  18414. ; Memory model selection set to 1 all others 0
  18415. ;
  18416. SMALLM EQU 0
  18417. C0MPACTM EQU 0
  18418. MEDIUMM EQU 0
  18419. LARGEM EQU 1
  18420. HUGEM EQU 0
  18421.  
  18422.  
  18423. STACK_SIZE EQU 1000H ;set desired stack size
  18424. _acrtused equ 1 ;satisfy external reference
  18425.  
  18426. PUBLIC_acrtused
  18427.  
  18428. DGROUP GROUP IDATA_BEG, _DATA, CONST, IDATA_END,\
  18429. UDATA_BEG, _BSS, STACK, UDATA_END
  18430.  
  18431. ; this segment marks beginning of rom code
  18432. ROMSTART_BEG SEGMENT BYTE 'CODE'
  18433. ROMSTART_BEG ENDS
  18434.  
  18435. IF SMALLM OR COMPACTM
  18436. _TEXT SEGMENT BYTE PUBLIC 'CODE'
  18437. EXTRN _main:NEAR ;main program
  18438. ASSUME CS:_TEXT
  18439.  
  18440. ENDIF
  18441. IF MEDIUMM OR LARGEM OR HUGEM
  18442. EXTRN _main:FAR ;main c program
  18443. _TEXT SEGMENT BYTE PUBLIC 'CODE'
  18444. ASSUME CS:_TEXT
  18445. ENDIF
  18446.  
  18447. ASSUME DS:DGROUP, SS:DGROUP
  18448. PUBLIC start
  18449.  
  18450. start PROC NEAR
  18451. cli
  18452. cld
  18453. ;*****************************************
  18454. ;
  18455. ; do hardware initialization and ram check
  18456. ;
  18457. ;*****************************************
  18458.  
  18459. PUBLIC init_ram
  18460.  
  18461. init_ram:
  18462. ;************
  18463. ; transfer initialize data from rom to ram
  18464. ;
  18465. MOV BX, SEG IDATA_BEG
  18466. MOV AX, SEG IDATA_END ;data to init.
  18467. sub ax,bx
  18468. mov cl,3
  18469. shl ax,cl
  18470. mov cx,ax
  18471. jcxz no_init_data
  18472.  
  18473. ;address of frame # in rom
  18474. mov ax, seg ENDCODE ;needed for jbromldr
  18475. inc ax ;ram init values begin at
  18476. mov ds.ax ;segment ENDCODE + 1
  18477. mov si,0
  18478. ;address of frame # in ram
  18479. mov ax, seg IDATA_BEG
  18480. mov es,ax
  18481. mov di,0
  18482.  
  18483. ; initialize data and const segments
  18484. rep movsw ;word transfer
  18485. no_init_data:
  18486. mov bx, seg UDATA_BEG ;clear bss data
  18487. mov ax, seg UDATA_END
  18488. sub ax, bx
  18489. mov cl,3
  18490. shl ax,cl
  18491. mov cx,ax
  18492. jcxz no_zerodata
  18493. mov es,bx
  18494. mov di,0
  18495. mov ax, 0
  18496. rep stosw
  18497. PUBLIC no_zerodata
  18498. no_zerodata:
  18499. mov ax,DGROUP ; set up stack
  18500. mov ds,ax
  18501. mov ss,ax
  18502. mov sp,OFFSET DGROUP:STACK_TOP
  18503.  
  18504. ;********************************************
  18505. ; add code to:
  18506. ; 1) initialize INITDATA Functions (see c0.asm)
  18507. ; 2) capture interrupt vectors 0-4
  18508. ;********************************************
  18509. sti ;enable interrupts
  18510. call _main ;enter main program
  18511. ;********************************************
  18512. ; add code to:
  18513. ; 1) shut down EXITDATA Functions
  18514. ; 2) handle shutdown errors
  18515. ; 3) prepare to restart
  18516. ;********************************************
  18517. jmp start
  18518. start ENDP
  18519. _abort PROC DIST
  18520. PUBLIC _abort
  18521. ;handle error abort
  18522. jmp start
  18523. _abort ENDP
  18524. IF SMALLM OR COMPACTM
  18525. _TEXT ENDS
  18526. ENDIF
  18527. IF MEDIUMM OR LARGEM OR HUGEM
  18528. _TEXT ENDS
  18529. ENDIF
  18530.  
  18531. ; segment marks end of rom code
  18532. ;make it non zero, length < 16 bytes
  18533. ENDCODE SEGMENT PARA PUBLIC 'ENDCODE'
  18534. db "ENDROM" ;seg ENDCODE+1 = begin ram
  18535. ENDCODE ENDS
  18536. ;**********************
  18537.  
  18538. ;beginning of dgroup and initialized data in ram
  18539.  
  18540. IDATA_BEG SEGMENT PARA PUBLIC 'IDATA_BEG'
  18541. IDATA_BEG ENDS
  18542.  
  18543.  
  18544. _FARDATA SEGMENT PARA PUBLIC 'FAR_DATA'
  18545. _FARDATA ENDS
  18546.  
  18547. _DATA SEGMENT PARA PUBLIC 'DATA'
  18548. _DATA ENDS
  18549.  
  18550. _CVTSEG SEGMENT WORD PUBLIC 'DATA'
  18551. PUBLIC _RealCvtVector
  18552. _RealCvtVector label word
  18553. _CVTSEG ENDS
  18554. _SCNSEG SEGMENT WORD PUBLIC 'DATA'
  18555. _SCNSEG ENDS
  18556. CONST SEGMENT WORD PUBLIC 'CONST'
  18557. CONST ENDS
  18558.  
  18559. _INIT_ SEGMENT WORD PUBLIC 'INITDATA'
  18560. _INIT_ ENDS
  18561. _INITEND_ SEGMENT WORD PUBLIC 'INITDATA'
  18562. _INITEND_ ENDS
  18563.  
  18564. _EXIT_ SEGMENT WORD PUBLIC 'EXITDATA'
  18565. _EXIT_ ENDS
  18566. _EXITEND_ SEGMENT WORD PUBLIC 'EXITDATA'
  18567. _EXITEND_ ENDS
  18568.  
  18569. IDATA_END SEGMENT PARA PUBLIC 'IDATA_END'
  18570. IDATA_END ENDS
  18571.  
  18572. ; end of initialized data
  18573.  
  18574. UDATA_BEG SEGMENT WORD PUBLIC 'UDATA_BEG'
  18575. UDATA_BEG ENDS
  18576.  
  18577. _BSSSEGMENT WORD PUBLIC 'BSS'
  18578. _BSSENDS
  18579. _BSSEND SEGMENT BYTE PUBLIC 'BSSEND'
  18580. _BSSEND ENDS
  18581.  
  18582. STACK SEGMENT PARA STACK 'STACK'
  18583. DW STACK_SIZE DUP (?)
  18584.  
  18585. STACK_TOP LABEL WORD
  18586. STACK ENDS
  18587.  
  18588. ; end of initialized segment in ram
  18589. UDATA_END SEGMENT WORD PUBLIC 'UDATA_END'
  18590. UDATA_END ENDS
  18591. ; bootstrap address for powerup reset
  18592. ; far jump to program beginning
  18593. ; may have to be manually placed in EPROM
  18594. ;BOOTSTRAP SEGMENT AT 0FFFFH
  18595. ; JMP FAR PTR start
  18596. ;
  18597. ;BOOTSTRAP ENDS
  18598. end start
  18599.  
  18600. ; End of File
  18601.  
  18602.  
  18603.  
  18604.  
  18605.  
  18606.  
  18607.  
  18608.  
  18609.  
  18610.  
  18611.  
  18612.  
  18613.  
  18614.  
  18615.  
  18616.  
  18617.  
  18618.  
  18619.  
  18620.  
  18621.  
  18622.  
  18623.  
  18624.  
  18625.  
  18626.  
  18627.  
  18628.  
  18629.  
  18630.  
  18631.  
  18632.  
  18633.  
  18634.  
  18635.  
  18636.  
  18637.  
  18638.  
  18639.  
  18640.  
  18641.  
  18642.  
  18643.  
  18644.  
  18645.  
  18646.  
  18647.  
  18648.  
  18649.  
  18650.  
  18651.  
  18652.  
  18653.  
  18654.  
  18655.  
  18656.  
  18657.  
  18658.  
  18659.  
  18660.  
  18661.  
  18662.  
  18663.  
  18664.  
  18665.  
  18666. A Fuzzy-Logic Torque Servo
  18667.  
  18668.  
  18669. Jack J. McCauley
  18670.  
  18671.  
  18672. Since receiving a BSEE from UC Berkeley, Jack J. McCauley has been working as
  18673. a Software Engineering Consultant. His specialty is real-time systems with an
  18674. emphasis on servo controls and signal processing. He can be reached at (510)
  18675. 531-1581.
  18676.  
  18677.  
  18678.  
  18679.  
  18680. Background
  18681.  
  18682.  
  18683. The developer of a control system desires a well-behaved system that is stable
  18684. throughout the operating spectrum. In the process of designing and debugging
  18685. the controller, operating regions that exhibit oscillatory or unstable
  18686. operation are avoided. Nonlinearities are typically modeled using a series of
  18687. discrete equations developed for the system.
  18688. A PID or Proportional Integral Differential controller is an example of a
  18689. closed-loop system. Implemented discretely, it would compute at z intervals:
  18690. T(z) = T(z-1) + Kk * (E(z) - E(z-1))
  18691. + Ki * E(z) + Kd * E(z-2) * (1 + E(z-1))
  18692. In this linear example, the z sampled periods for the controller represent
  18693. discrete time samples and Kk, Ki, and Kd are constants. A nonlinear system
  18694. designer might attempt to modify Kk, Ki, and Kd as a function of input E(z).
  18695. This of course would require Kk(z), Ki(z), and Kd(z) (and possibly Kk(z-1,
  18696. z-2, .... z-n) etc). The designer would compute stability and phase margin in
  18697. a continuous system and convert these to the discrete world using a Discrete
  18698. Transform.
  18699. After the analysis the designer would proceed to code T(z) into assembler or C
  18700. and port it to a target. This article describes how to implement a simple
  18701. fuzzy-logic based servo controller in the C programming language. C language
  18702. portability allows the controller to run in either a micro-controller based
  18703. environment or on a PC.
  18704.  
  18705.  
  18706. Escaping Brittleness
  18707.  
  18708.  
  18709. Brittleness is the bane of the control-system designer. A brittle system is
  18710. one which "breaks" easily. For example, in the coding of T(z), we defined the
  18711. output T(z) to be an eight-bit unsigned char that follows the eight-bit radix
  18712. of our A/D converter. Suppose that several of the terms of T(z) were
  18713. discreetly nine or ten bits. The result could be truncated and thus produce
  18714. incorrect output when T(z) was cast to an unsigned char. We must check
  18715. overflow after every operation of T(z) and perform corrective logical
  18716. operations.
  18717. Besides the implementation issues, PID and PI suffer from some serious
  18718. real-world problems. In controls, non-linearities are the rule. Factors such
  18719. as friction and temperature affect the behavior of systems. What appears to be
  18720. stable under one set of conditions yields poor performance under another.
  18721. There are techniques for dealing with changes. One possibility is to
  18722. sliding-mode modify the PI gain parameters. Experience with this approach has
  18723. shown that it works very well so long as the inputs are bounded properly.
  18724. Current thinking, however, indicates that the amount of time invested in
  18725. developing this approach will not be extracted in performance. What most
  18726. people do to deal with non-linearities is to add a bunch of testing and
  18727. branching code to deal with the caveats. In most PID, PI closed-loop systems,
  18728. it ends up that the majority of the code is dedicated to dealing with these
  18729. anomalies.
  18730.  
  18731.  
  18732. Fuzzy Logic
  18733.  
  18734.  
  18735. Fuzzy logic holds promise as a means of handling control-system
  18736. non-linearities. It provides a unique way of looking at control problems. The
  18737. control problem is described as a rule base, with a rule input matrix m(T) and
  18738. a corresponding output function for that rule. When few rules are to be
  18739. evaluated, simple linguistic rules can be substituted instead.
  18740. One might imagine a fuzzy set m(T) that describes the outside temperature as
  18741. "very cold," "cold," "warm," and "hot." The four members of m(T) are
  18742. linguistic operators that represent the human inferred description of the
  18743. space representation of each member of m(T). The membership function is
  18744. typically a trapezoid that represents a degree of membership. The degree of
  18745. membership is a real number between 0.0 and 1.0 with a degree of 1.0 meaning
  18746. full membership and a degree 0.0 indicating no membership. (See Figure 1.)
  18747. Suppose the outside temperature is --50 F. Then according to m(T) the outside
  18748. temperature will lie in the domain of "very cold." If the temperature is 12 F,
  18749. the temperature might not be so much "very cold" as it is just "cold."
  18750. Membership functions almost always overlap in the domain of m(T). Fuzzy logic
  18751. provides a means of dealing with overlapping domains and also domains of
  18752. overlapping output sets. The math allows us to weigh the cumulative effect of
  18753. all rules to generate a crisp output. A crisp output is also called a
  18754. de-fuzzified output.
  18755. The typical fuzzy plant (controller) consists of one or more input fuzzy sets,
  18756. a rule base, and an output fuzzy set. The input sets fuzzify using the rule
  18757. base, and the output set de-fuzzifies from the inputs, rules, and the output
  18758. de-fuzzifier function.
  18759.  
  18760.  
  18761. An Application
  18762.  
  18763.  
  18764. This two-input controller is a torque regulator on a DC motor. The controller
  18765. topology could easily be extended to any two-input, single-output fuzzy
  18766. controller.
  18767. We desire to regulate the torque applied to the shaft of a permanent-magnet DC
  18768. brush motor. The torque Tmotor applied to the shaft is a linear function of
  18769. armature current Imotor and (to a lesser extent which will be ignored here)
  18770. motor temperature. Armature current is controlled by a Pulse Width Modulated
  18771. (PWM) amplifier which varies the duty cycle DA from 0 to 100 per cent as a
  18772. linear function of input voltage.
  18773. If a positive voltage is applied to the motor terminals, a positive torque
  18774. will be applied to the motor shaft. If a negative voltage is applied, a
  18775. negative torque will be applied to the motor shaft. The job of the servo is to
  18776. regulate the torque on the motor shaft under varying load conditions. The
  18777. servo applies an adjustment to DA based on the commanded torque input,
  18778. Tcmd(z), and the feedback torque input:
  18779. Tarm = Imotor(z) * Kmotor(z)
  18780. sensed on the motor shaft. (See Figure 2.)
  18781. The eight-bit PWM applies a positive voltage to the terminals if the PWM value
  18782. is between 128 and 255. A negative voltage is applied if the value is between
  18783. 0 and 126. An eight-bit A/D converter and pre-amp sense the feedback torque.
  18784. The granularity of the feedback torque is determined by the radix of an
  18785. eight-bit A/D converter. An eight-bit D/A (PWM) assures coherency between
  18786. conversion radixes on the input. (See Figure 3.)
  18787.  
  18788.  
  18789. Fuzzy Controller for Torque
  18790.  
  18791.  
  18792. The fuzzy controller is implemented in C on an inexpensive high-integration
  18793. micro-controller. A simulation program executes on a PC-AT, to assist
  18794. debugging of the fuzzy servo prior to porting it to the micro-controller.
  18795. In this system the fuzzy plant is a two-input, single-output controller. Three
  18796. fuzzy sets exist with the input sets occupying two dimensions of the rule base
  18797. and the output set occupying the action to be performed on each rule, which is
  18798. the combination pointed to by the input fuzzy set membership functions.
  18799. The first input fuzzy variable Terror is the error between the setpoint or
  18800. command torque at time t, and the feedback torque or:
  18801. Terror(z) = Tcmd(z)- Tarm(z)
  18802.  
  18803. The second input dTerror/dt is the "rate of change of error" or how quickly
  18804. the feedback torque at time t is changing with respect to the previous sample
  18805. at time t--1:
  18806. dTerr(z)/dt = Tarm(z) - Tarm(z-1)
  18807. The output function m(DA) determines the action to be performed upon
  18808. evaluation of the rules. For example, if the instantaneous feedback torque is
  18809. at the setpoint:
  18810. Tcmd(z) ~ Tarm(z)
  18811. but the feedback torque derivative is non-zero:
  18812. Tarm(z) > Tarm(z-1)
  18813. we might wish to apply a slight adjustment to DA so that Terror(z) and
  18814. dTerr(z)/dt remain zero. The amount of adjustment applied to DA is determined
  18815. by the inputs and also the output control fuzzy set m(DA). Which membership
  18816. function of m(DA) to apply is determined by the input membership function
  18817. space and the operator-inferred description of the control problem in the
  18818. rule-base table. The idea is to maintain zero error and zero error derivative:
  18819. dTerr(z)/dt = Terror(z) = 0
  18820. The number of inputs and the dimension of each determines the rule base
  18821. matrix. For example, we have two input fuzzy sets, m(T(Verr) and m(dTerr/dt),
  18822. with five membership functions each. This yields a 5x5 or 25 rule-base
  18823. controller. The corresponding action to be performed by the evaluation of each
  18824. input rule determines the output controlling membership function m(DA). For
  18825. example, rule (3, 3) is:
  18826. IF Terror == M
  18827. AND dTerr/dt == -M
  18828. THEN DA == Z
  18829. and rule (3, 2) is:
  18830. IF Terror == M
  18831. AND dTerr/dt == Z
  18832. THEN DA == -M
  18833. A literal evaluation of rule (3,2) would read, "If the feedback torque is less
  18834. than the setpoint, and the feedback torque is approaching the setpoint at
  18835. medium rate, then apply zero bias to the PWM." The key to fuzzy logic control
  18836. is that the degrees of membership incorporate the amount of bias to apply and
  18837. the degree of truth to each rule. (See Figure 4.)
  18838.  
  18839.  
  18840. When a Rule Fires
  18841.  
  18842.  
  18843. A rule fires when a nonzero result is returned upon evaluation of the rule.
  18844. The degree to which the rule is true is incorporated in the membership
  18845. functions. If the input values lie within the membership function trapezoid
  18846. space, then the input is valid. A rule fires when both inputs to the rule are
  18847. non-zero. A rule is evaluated using ternary operators similar in concept to
  18848. Boolean operators. The operators logically follow the AND, OR, and NOT
  18849. constructs.
  18850. Rules and rule bases can be built using the operators exactly like those in
  18851. standard Boolean logic, but because membership functions deal with degrees of
  18852. truth we must have a mechanism for evaluating the operators:
  18853. AND == MINIMUM(Input x, Input y)
  18854. OR == MAXIMUM(Input x, Input y)
  18855. NOT == 1.0 -Input x
  18856. The AND operator returns the minimum of the result of a rule that has fired.
  18857. The OR operator returns the maximum of the result of a rule that has fired.
  18858. The NOT operator returns the complement of its input x, which is roughly the
  18859. analog of ~x in C.
  18860. In this application the AND operator is used exclusively to operate on the
  18861. rule table inputs, and the normalized 0.0 -> 1.0 values are actually
  18862. normalized eight bit values from 0 to 255.
  18863. (let dTerr(z)/dt = -11 and Terr(z) = 90)
  18864. In evaluating the fired rules (3, 3) and (3, 2) shown above, we first
  18865. determined if the inputs lie within the domain of m(Terror) and m(dTerror/dt)
  18866. by examining membership functions for m(Terror) and m(dTerror/dt). If the rule
  18867. fires, we take the a cut for each of the input membership functions M and Z.
  18868. The a cut graphically slices the top of the output controlling membership
  18869. function. The AND operator returns the lesser of the two a cuts for the inputs
  18870. m(dTerror/dt, L) and m(Terror, M) for use in the slicing operation. Because of
  18871. the overlapping domain of the membership functions, more than one rule will
  18872. typically fire. When this occurs, fuzzy logic provides a mathematical method
  18873. for dealing with multiple rule findings. the output of each rule is an output
  18874. membership functions that has been a cut and is then graphically ORed with the
  18875. accumulated preceding output membership functions. The resultant resembels a
  18876. "shadow" of each fired rule with the accumulated output overlapping each
  18877. preceding fired rule. Mathematically, the accumulation is the fuzzy OR
  18878. operator (MAXIMUM) applied to the current rule that fires and the accumulated
  18879. fired rules. (See figure 5.)
  18880.  
  18881.  
  18882. De-fuzzification
  18883.  
  18884.  
  18885. The accumulated result of all firing rules occupies a two-dimensional space
  18886. which is a fuzzy set with one membership function. The resultant must be
  18887. converted to real world crisp result which takes into account all applied
  18888. rules. To do this, a spatial average taken which computes the center of area
  18889. (COA) of the function. COA is among several methods for computing the average.
  18890. It is the method used in this application because it executes quickly.
  18891. COA = (25*18 + 25*31 + 128*100
  18892. + 128*127 + 128*140)
  18893. /(25 + 25+ 128 + 128 + 128) = 74
  18894. So the DA value written to the PWM/D/Ais 74.
  18895.  
  18896.  
  18897. C Language Implementation
  18898.  
  18899.  
  18900. The membership functions use table look up to follow the example:
  18901. m(dTerror/dt) (in oz-in):
  18902. -L WHEN dTerr(z)/dt<=-35
  18903. -M WHEN -50 <= dTerr(z)/dt =< 0
  18904. ZM WHEN -10 > = dTerr(z)/dt <= 7
  18905. M WHEN 0 <= dTerr(z)/dt <= 50
  18906. L WHEN dTerr(z)/dt >= 10
  18907. All 25 rules evaluated and the rules that fire result in a output according to
  18908. the COA output de-fuzzifier function. The output array stores the result of
  18909. the rule firing and the history of predicate rules. Conflicts are resolved
  18910. using the fuzzy OR MAXIMUM) operator. The resultant outputs contribute to a
  18911. weighted sum to yield the COA output. This strategy allows for the quick
  18912. computation of a crisp value using the eight-bit multiples.
  18913. For a micro-controller application, It was determined by experimentation that
  18914. a loop repetition rate of at least 1 KHz would be required for satisfactory
  18915. performance. Command set-points were entered through a serial port and an
  18916. oscilloscope measured the response of the loop. Figure 6 shows the measured
  18917. step response, which is quite adequate.
  18918.  
  18919.  
  18920. Conclusion
  18921.  
  18922.  
  18923.  
  18924. Fuzzy logic control seems to hold promise as an alternative to standard PID
  18925. control. Rigid modeling is not required because the servo tuning is done via
  18926. the fuzzy set membership functions and the rules. The rules incorporate
  18927. Boolean statements and operator-inferred control rather than strict
  18928. theoretical modeling. The membership functions allow tuning of the control
  18929. system by altering the space that they occupy, and the placements of the
  18930. output membership functions provide the control action to be performed by the
  18931. rule. The rules use Boolean-like operators that infer a control action
  18932. depending upon the degree of truth of the rule firing. A rule fires if the
  18933. fuzzified inputs return a non-zero degree of truth, referred to as the a cut
  18934. for each input.
  18935. Fuzzy logic is not as brittle as standard PID control because of the natural
  18936. language approach to the control problem. Second-order effects such as
  18937. temperature can be incorporated directly in the rule by logically appending
  18938. another fuzzy set for temperature onto the rule. Suppose that we know that an
  18939. increase in temperature causes the torque to change much more rapidly. By
  18940. creating a rule base that incorporates temperature, we compensate for the
  18941. change, as in:
  18942. IF Terror == M
  18943. AND dTerr/dt == -M
  18944. AND Temperature == L
  18945. THEN DA = M
  18946. In PID control second-order variables such as temperatures are not so easily
  18947. incorporated into the model.
  18948. The fuzzy controller is also less prone to radix overflow and underflow
  18949. problems. In this application, the controller was implemented using eight-bit
  18950. LUTs. All mathematical operations were cast to 32-bit long, thus preventing
  18951. overflow. Because of the averaging nature of COA, it is inherently safe from
  18952. overflow.
  18953. Figure 1 Fuzzy set for temperature
  18954. Figure 2 Feedback configuration of a typical torque servo
  18955. Figure 3 Fuzzy plant for torque servo
  18956. Figure 4 Fuzzifier rules and membership functions for 8-bit system
  18957. Figure 5 Example of Fuzzify >=Defuzzify when two rules fire
  18958. Figure 6 An illustration of de-fuzzification
  18959.  
  18960. Listing 1 C declaration code for rule table and membership functions
  18961. /**************************************************************
  18962. File: fuzzy.h
  18963. Date: 4/3/93
  18964. Author: Jack J. McCauley
  18965. Header file for fuzzy.c
  18966. **************************************************************/
  18967.  
  18968. /* membership function size */
  18969. #define TORQUE_MEMBERS 5
  18970. #define DER_MEMBERS 5
  18971.  
  18972. /* loop frequency */
  18973. #define LOOP_HZ 1
  18974.  
  18975. /* integrator constant z-1*/
  18976. #define SKIP 1
  18977.  
  18978. /* normalized value */
  18979. #define NORMAL 255
  18980.  
  18981. /* Current ma */
  18982. #define MAX_TORQUE 1000
  18983. #define MIN_TORQUE -1000
  18984.  
  18985. /* derivative */
  18986. #define MAX_DERI LOOP_HZ*MAX_TORQUE
  18987. #define MIN_DERI LOOP_HZ*MIN_TORQUE
  18988.  
  18989. /* pwm 1/10 % */
  18990. #define MAX_PWM 255
  18991. #define MIN_PWM 0
  18992.  
  18993. /* Max and Min */
  18994. #define MIN_ERROR 0
  18995. #define MAX_ERROR 255
  18996.  
  18997. /* size of all arrays */
  18998. #define ARRAY_SIZE 256
  18999.  
  19000. /* represent a normalized 0 -> 1000 = 0.0 -> 1.0 */
  19001. #define FUZZY_RADIX NORMAL
  19002.  
  19003.  
  19004. /* fuzzy max (ternary) operator */
  19005. #define FUZ_MAX( x, y ) ((x>y) ? x : y)
  19006.  
  19007. /* fuzzy min (ternary) operator */
  19008. #define FUZ_MIN( x, y ) ((x<y) ? x : y)
  19009.  
  19010. /* fuzzy AND operator */
  19011. #define FUZ_AND( x, y ) FUZ_MIN( x, y )
  19012.  
  19013. /* fuzzy OR operator */
  19014. #define FUZ_OR( x, y ) FUZ_MAX( x, y )
  19015.  
  19016. /* fuzzy compliment operator */
  19017. #define FUZ_NOT( x ) ( FUZZY_RADIX - x )
  19018.  
  19019. /* create the membership function space */
  19020. /* first is the torque error fuzzy set */
  19021. struct s_torq_members {
  19022. int neg_large[ARRAY_SIZE];
  19023. int neg_med[ARRAY_SIZE];
  19024. int zero[ARRAY_SIZE];
  19025. int pos_med[ARRAY_SIZE];
  19026. int pos_large[ARRAY_SIZE];
  19027. float slope;
  19028. float intercept;
  19029. } torq_members;
  19030.  
  19031. /* next is the rate of change fuzzy set torque error */
  19032. struct s_deri_members {
  19033. int pos_large[ARRAY_SIZE];
  19034. int pos_med[ARRAY_SIZE];
  19035. int zero[ARRAY_SIZE];
  19036. int neg_med[ARRAY_SIZE];
  19037. int neg_large[ARRAY_SIZE];
  19038. float slope;
  19039. float intercept;
  19040. } deri_members;
  19041.  
  19042. /* last is the PWM fuzzy set ouput */
  19043. struct s_pwm_members {
  19044. int neg_large[ARRAY_SIZE];
  19045. int neg_med[ARRAY_SIZE];
  19046. int zero[ARRAY_SIZE];
  19047. int pos_med[ARRAY_SIZE];
  19048. int pos_large[ARRAY_SIZE];
  19049. float slope;
  19050. float intercept;
  19051. } pwm_members;
  19052.  
  19053. /* define output rule table members */
  19054. #define L pwm_members.pos_large
  19055. #define M pwm_members.pos_med
  19056. #define Z pwm_members.zero
  19057. #define NM pwm_members.neg_med
  19058. #define NL pwm_members.neg_large
  19059.  
  19060. /* finally the fuzzy sets */
  19061. /* torque feedback error */
  19062. int *dTerr_dt[] = {
  19063.  
  19064. deri_members.pos_large ,
  19065. deri_members.pos_med ,
  19066. deri_members.zero ,
  19067. deri_members.neg_med ,
  19068. deri_members.neg_large ,
  19069. };
  19070. /* torque (OZ-in) */
  19071. int *Terror[] = {
  19072. torq_members.neg_large ,
  19073. torq_members.neg_med ,
  19074. torq_members.zero ,
  19075. torq_members.pos_med ,
  19076. torq_members.pos_large ,
  19077. }
  19078.  
  19079. /* create the rule table and allocate space */
  19080. struct s_rule {
  19081. int *table[TORQUE_MEMBERS];
  19082. } rule[DER_MEMBERS] =
  19083. /* */
  19084. {{Z, NM, NM, NM, NL },
  19085. {M, Z, NM, NM, NL },
  19086. { L, M, Z, NM, NL },
  19087. { L, M, M, Z, NM },
  19088. { L, M, M, M, Z }};
  19089.  
  19090. The Fuzzy membership functions are stored in a look up table for all input
  19091. and output fuzzy sets. Fuzzy variables with three digits of precision are
  19092. normalized between 0 and 255 integer corresponding to an 8-bit radix converter
  19093. and a fuzzy set 0.0 - 1.0 normalized value. Look up tables increase bandwidth
  19094. and allow the servo to run with few floating point calculations.
  19095.  
  19096. /****************************************************
  19097. File: fuzzy.c
  19098. Date: 4/3/93
  19099. Author: Jack J. McCauley
  19100. fuzzy torque controller for motor
  19101. ****************************************************/
  19102.  
  19103. #include "stdio.h"
  19104. #include "stdlib.h"
  19105. #include "conio.h"
  19106. #include "proto.h"
  19107. #include "fuzz.h" /* the above file */
  19108.  
  19109. /****************************************************
  19110. Routine: calc_slope
  19111. Date: 4/3/93
  19112. Author: Jack J. McCauley
  19113. calcs line slope of two points in a plane
  19114. *****************************************************/
  19115. float calc_slope( float x1, float x2, float y1, float y2 ) {
  19116.  
  19117. float slope;
  19118.  
  19119. if ( x1 == x2 )
  19120. slope = 10000000.0;
  19121. else if( y1 == y2 )
  19122. slope = 0.0;
  19123.  
  19124. else {
  19125. slope = (y1 - y2)/(x1 - x2);
  19126. if( slope > 100000000.0 )
  19127. slope = 100000000.0;
  19128. }
  19129. return( slope );
  19130. }
  19131. /*****************************************************
  19132. Routine: calc_intercept
  19133. Date: 4/3/93
  19134. Author: Jack J. McCauley
  19135. calcs line intercept of two points in a plane
  19136. *****************************************************/
  19137. float calc_intercept( float x1, float x2, float y1, float y2 ) {
  19138.  
  19139. float intercept;
  19140.  
  19141. if ( x1 == x2 )
  19142. intercept = 100000000.0;
  19143. else {
  19144. intercept = (y2*x1 - y1*x2)/(x1 - x2);
  19145. if( intercept > 100000000.0 )
  19146. intercept = 100000000.0;
  19147. }
  19148. return( intercept );
  19149. }
  19150. /* end print_array */
  19151.  
  19152. /**********************************************************
  19153. Routine: down_load()
  19154. Date: 4/3/93
  19155. Author: Jack J. McCauley
  19156. initialize fuzzy membership function tables from serial port DRIVER not shown
  19157. ***********************************************************/
  19158. void down_load( int *membership_function, int len )
  19159. {
  19160. int k;
  19161. char buff[32];
  19162.  
  19163. /*short, tight loop */
  19164. for( k=0; k< len; k++ ) {
  19165. /* get ascci string from driver */
  19166. gets( buff, 12 );
  19167. *membership_function++: atoi( buff );
  19168. }
  19169. }
  19170. /* end down_load() */
  19171. /********************************************************
  19172. Routine: fuzzy_init
  19173. Date: 4/3/93
  19174. Author: Jack J. McCauley
  19175. initialize fuzzy membership function tables
  19176. *********************************************************/
  19177. void fuzzy_init( void )
  19178. {
  19179. int k;
  19180. long slope;
  19181. long intercept;
  19182. int val;
  19183.  
  19184.  
  19185. /*
  19186. There are several ways to generate these tables:
  19187.  
  19188. 1) The easiest is to simply use ROM space and store them permanently in
  19189. memory. FUZZ.H
  19190. would need to be modified to reflect the static ROM delecerations for each
  19191. fuzzy
  19192. set and the values would be initialize directly attaching them to the data
  19193. arrays.
  19194.  
  19195. 2) Down load the fuzzy sets through the serial port as I did in the
  19196. tuning of this servo. In which case I've included that code here (of
  19197. course in the ROM version of the system you'll need 1)).
  19198.  
  19199. 3) Store the slope-intercept form of each membership function and
  19200. calculate the line slopes and intercepts
  19201. */
  19202.  
  19203. /*
  19204. Membership functions are downloaded through the serial port from a file in
  19205. ASCII
  19206. format one set member at a time. In this system I used a spreadsheet and
  19207. graphical interface to actually draw the membership functions with a mouse.
  19208. This aided in tuning the servo substantailly. In the ROH version of the
  19209. system,
  19210. I wrote a small programm to append the RDM statics memberships to fuzz.h.
  19211. */
  19212. down_load( deri_members.neg_large, ARRAY_SIZE);
  19213. down_load( torq_members.neg_large, ARRAY_SIZE);
  19214. down_load( pwm_members.neg_large, ARRAY_SIZE);
  19215. down_load( deri_members.neg_med, ARRAY_SIZE);
  19216. down_load( torq_members.neg_med, ARRAY_SIZE);
  19217. down_load( deri_members.zero, ARRAY_SIZE);
  19218. down_load( torq_members.zero, ARRAY_SIZE);
  19219. down_load( pwm_members.zero, ARRAY_SIZE);
  19220. down_load( deri_members.pos_med, ARRAY_SIZE);
  19221. down_load( orq_members.pos_med, ARRAY_SIZE);
  19222. down_load( pwm_members.pos_med, ARRAY_SIZE);
  19223. down_load( deri_members.pos_large, ARRAY_SIZE);
  19224. down_load( torq_members.pos_large, ARRAY_SIZE);
  19225. down_load( pwm_members.pos_large, ARRAY_SIZE);
  19226.  
  19227. /* slope intercept line calculation */
  19228.  
  19229. /* these line equations are used for scaling the
  19230. crisp values from the above arrays */
  19231. deri_members.slope = calc_slope(MIN_DERI, MAX_DERI, 0, ARRAY_SIZE-1 );
  19232. deri_members.intercept = calc_intercept(MIN_DERI, MAX_DERI, 0, ARRAY_SIZE-1
  19233.  
  19234. torq_members.slope: calc_slope( MIN_TORQUE, MAX_TORQUE, 0, ARRAY SIZE-1 );
  19235. torq_members.intercept = calc_intercept(MIN_TORQUE, MAX_TORQUE, 0,
  19236. ARRAY_SIZE-1);
  19237.  
  19238. pwm_members.slope = calc_slope(0, ARRAY_SIZE-1, MIN_PWM, MAX_PWM);
  19239. pwm_members.intercept = calc_intercept(0, ARRAY_SIZE-1, MIN_PWM, MAX_PWM);
  19240. /*
  19241. for( k=0; k<ARRAY_SIZE; k++ )
  19242. printf("%d %4d %4d %4d %4d
  19243. %4d\n["],k,deri_members.pos_large[k],deri_members.pos_med[k],
  19244. deri_members.zero[k],deri_members.neg_med[k],deri_memb
  19245. ers.neg_large[k]);
  19246. */
  19247. }
  19248. /* end FUZZINIT */
  19249.  
  19250.  
  19251. /***********************************************
  19252. Routine: defuzzify_COA
  19253. Date: 4/3/93
  19254. Author: Jack J. McCauley
  19255. defuzzify using COA
  19256. **********************************************/
  19257. float defuzzify_COA( int *output ) {
  19258.  
  19259. static long k;
  19260. static long numerator, denominator, val;
  19261.  
  19262. numerator = 0;
  19263. denominator = 0;
  19264.  
  19265. for ( k=0; k<ARRAY_SIZE; k+=SKIP ) {
  19266.  
  19267. val = (long)*output;
  19268. output+=SKIP;
  19269. /* look for non-zero values and include in our COA calc */
  19270. if( val ) {
  19271. denominator += val;
  19272. /* ROM based later */
  19273. numerator += val *(long)k;
  19274. }
  19275. }
  19276. /* divide if non-zero x/D */
  19277. if( denominator ) {
  19278. /* return crisp value */
  19279. /* Could be converted from float if desired */
  19280. numerator = (long)((float)(numerator/denominator) * pwm_members.slope +
  19281. pwm_members.intercept);
  19282. return( numerator );
  19283. } else
  19284. return( 0 );
  19285.  
  19286. }
  19287. /* end COA calc */
  19288.  
  19289. /******************************************************
  19290. Routine: fuzzify
  19291. Date: 4/3/93
  19292. Author: Jack J. McCauley
  19293. fuzzifier for our servo
  19294. ******************************************************/
  19295. int fuzzify( int derr_dt, int err,
  19296. int *p_Terror, int *p_dTerror,
  19297. int *p_out, int *resultant ) {
  19298.  
  19299. static int k, val, alpha_cut, temp, *moe;
  19300.  
  19301. /* COA method */
  19302. /*
  19303. Get the alpha cut for error and derivative. Use the AND (min) operator and cut
  19304. the
  19305. output , a non-zero fuzzy MIN indicates a rule has fired , write the cut to
  19306. the result
  19307. array by "shadowing" the exisiting using the MAX operator
  19308. */
  19309.  
  19310. /* normalize the output derivative */
  19311.  
  19312. moe: resultant;
  19313.  
  19314. /* Find out if the rule fired. If the rule fired then get the alpha cut */
  19315. if( (alpha_cut = FUZ_AND( p_dTerror[derr_dt], p_Terror[err] )) != 0 ) {
  19316. for ( k=0; k<ARRAY_SIZE; k+=SKIP ) {
  19317. /*
  19318. An interesting effect will be noticed if the skip is set greater than
  19319. 1. In this system setting skip to lets say 2 or three will yield
  19320. in most circumstance yield the same defuzzified crisp output if the
  19321. slope of the membership functions are not too steep.
  19322. The other benifit is that the execution speed is increased greatly
  19323. */
  19324. /* create shadow */
  19325. val = *p_out;
  19326. /* don't get bit by shortcuts *./
  19327. val = FUZ=MIN( alpha_cut, val );
  19328. temp = *resultant;
  19329. * resultant = FUZ_MAX( temp, val );
  19330. resultant+=SKIP;
  19331. p_out+=SKIP;
  19332. }
  19333. /* rule fired */
  19334. return( 1 );
  19335. } else
  19336. /* rule didn't fire */
  19337. return( 0 );
  19338. }
  19339. /* end FUZZIFICATION calc */
  19340.  
  19341. /********************************************************
  19342. Routine: servo_torque
  19343. Date: 4/3/93
  19344. Author: Jack J. McCauley
  19345. fuzzy servos to torque set point
  19346. ********************************************************/
  19347. /*
  19348. Routine would run as an ISR attched to a timer interrupt passed values are
  19349. read
  19350. from and A/D convertor and command torque (serial port etc..)
  19351. */
  19352. long servo_torque( int error, int derr_dt )
  19353. {
  19354. /* statics for speed */
  19355. static int row, column;
  19356.  
  19357. /* pointers to tables */
  19358. static int *p_out, *p_dTerr_dt, *p_Terror;
  19359.  
  19360. /* COA container class */
  19361. static int resultant[ARRAY_SIZE];
  19362.  
  19363. /* zero fill the array */
  19364. for( row = 0; row<ARRAY_SIZE; row++ )
  19365. resultant[row] = 0;
  19366.  
  19367. /* normalizing the error derivative will allow the error to ff
  19368. /* normalize error derivative to look up table RADIX*/
  19369. error = (long)((float)error * torq_members.slope + torq_members.intercept);
  19370.  
  19371. /* normalize error derivative*/
  19372.  
  19373. derr_dt = (long)((float)derr_dt*deri_members.slope + deri_members.intercept);
  19374.  
  19375. /* MAX and MIN error */
  19376. if( error > MAX_ERROR )
  19377. error = MAX_ERROR;
  19378. else if( error < MIN_ERROR )
  19379. error = MIN_ERROR;
  19380.  
  19381. /* MAX and MIN derivative */
  19382. if( derr_dt > MAX_DERI )
  19383. derr_dt = MAX_DERI;
  19384. else if( derr_dt < MIN_DERI )
  19385. derr_dt = MIN_DERI;
  19386.  
  19387. /* traverse the rule table and evaluate the rules */
  19388. for( row = 0; row < DER_MEMBERS; row++ ) {
  19389.  
  19390. /* get pointers to tables from data structures */
  19391. p_dTerr_dt = dTerr_dt[row];
  19392. for( column = 0; column < TORQUE_MEMBERS; column++ ) {
  19393.  
  19394. /* get pointers to tables from data structures */
  19395. /* get the output membership function for that rule evaluation */
  19396. p_out = rule[row].table[column];
  19397. p_Terror = Terror[column];
  19398. /* fuzzify the rule */
  19399. fuzzify( derr_dt, error, p_Terror, p_dTerr_dt, p_out, resultant );
  19400. }
  19401. }
  19402.  
  19403. /* defuzzify using COA */
  19404. return( defuzzify_COA( resultant ) );
  19405.  
  19406. /* return the fired rules */
  19407. }
  19408.  
  19409. /*END */
  19410.  
  19411.  
  19412.  
  19413.  
  19414.  
  19415.  
  19416.  
  19417.  
  19418.  
  19419.  
  19420.  
  19421.  
  19422.  
  19423.  
  19424.  
  19425.  
  19426.  
  19427.  
  19428.  
  19429.  
  19430.  
  19431.  
  19432.  
  19433.  
  19434.  
  19435.  
  19436. Weight Reduction Techniques in C++
  19437.  
  19438.  
  19439. Randy Kamradt
  19440.  
  19441.  
  19442. Randy Kamradt has been programming in C/C++ for the past seven years. He is
  19443. currently working for TEAM Software, a fashionably lean consutling company
  19444. developing an integrated database package for the Ventura County
  19445. Superintendent of Schools, in Ventura California.
  19446.  
  19447.  
  19448.  
  19449.  
  19450. Introduction
  19451.  
  19452.  
  19453. I have been working on a large scale database downsizing project for the
  19454. Ventura County School District since the beginning of 1993. Recently we turned
  19455. over to the users the first application, a teaching-credential tracking
  19456. database. Instead of the ticker tape parade we had expected, all they did was
  19457. comment on how slow it seemed to run. Even though we used all the latest buzz
  19458. words -- such as "object-oriented" and "client-server" -- we had to admit it
  19459. was very sluggish. So we began an intense optimization effort, putting our
  19460. application on a crash course of exercise and diet, to get it down to size and
  19461. up to speed. This article deals with the potential problems facing
  19462. object-oriented programming in C++ and some of the solutions we used.
  19463. One of the major advantages to object-oriented programming is that it can hide
  19464. complexity. Very complicated sub-systems can be wrapped by classes. These
  19465. classes provide well defined interactions, making their objects relatively
  19466. immune to environmental factors. The down side to this is that the application
  19467. programmer may not be aware of all the ramifications of using an object.
  19468. Objects may encapsulate the acquisition of system resources, or they may
  19469. create sub-objects. Just declaring a local variable can be a costly procedure.
  19470. One problem that faced us early on was from a class of the Commonbase Database
  19471. class library, DBTable. Just creating a variable of type DBTable had the side
  19472. effect of opening a communication channel (socket) to the database server. We
  19473. were allocating DBTable objects from free store and in rare cases they never
  19474. got deleted. Since our networking software had only 20 sockets available, the
  19475. application would run fine for a while and then run out of sockets. This is
  19476. actually an old problem, familiar to C programmers, but with a new twist: more
  19477. than memory might be allocated by creating a user-defined type. Since this
  19478. caused the program to die it was fixed early on, but other similar problems
  19479. existed. We had to automate the creation of each DBTable, so as not to create
  19480. it until it was needed, and to delete it as soon as possible. It could not be
  19481. left up to the application programmer.
  19482. Another side of complexity hiding is that an object may be a front for many
  19483. sub-objects. One of our objects, BMOJoin (Business Model Object Join),
  19484. contained a list of DBTables. A join is database jargon for a relational
  19485. combination of tables. Since a DBTable holds one socket, a BMOJoin can hold
  19486. many. To add to this problem, another object, BMOIterator has a pointer to a
  19487. BMOJoin. As I will explain later, the BMOIterator was the key to controlling
  19488. all these resources, and minimizing their allocation.
  19489. Another class, ACond, represents a logical condition. It is implemented as an
  19490. expression tree. So doing a simple assignment of an ACond could be costly.
  19491. Again this is actually an old problem, the only difference being that in C
  19492. such copying can be done only by calling a function, while in C++ the function
  19493. can be initiated with an assignment operator. C programmers know that calling
  19494. a function can take time and resources, but using built-in operators always
  19495. takes (relatively small) constant time and never takes system resources. These
  19496. assumptions go out the window in C++.
  19497. Free-store usage is another problem. Just opening a window and tieing it to a
  19498. database in our application takes thousands of memory allocations, some of
  19499. them just for a few bytes. Memory allocation is the lifeblood of C++
  19500. programming because objects should be made autonomous, and cannot rely on
  19501. outside sources such as local or global variables for their internal memory
  19502. space.
  19503. A string class, for instance, typically contains a pointer to memory
  19504. containing the string (see Figure 1). That memory must come from free store if
  19505. the class is to be considered general purpose. If the memory came from outside
  19506. the class, the string could not delete it when it was done, and the user would
  19507. have to delete it if necessary. The issue of pointer ownership is important in
  19508. making a safe application, and helps alleviate some of the common C pointer
  19509. troubles such as deleting memory twice, or forgetting to delete it at all. It
  19510. also means that memory allocation and copying may be done more often than
  19511. absolutely necessary.
  19512. Another problem introduced with object-oriented programming is
  19513. over-generalization. With base classes one can provide the common subset of
  19514. functions available from a set of derived classes. If the programmer uses the
  19515. base class, the full set of functions may not be available. An example of this
  19516. is the use of a list object. A list object might be represented either by a
  19517. linked list or by an array. If the programmer uses a list and needs to get to
  19518. the n-th object, indexing may not be an option. The programmer will have to
  19519. iterate through the list, keeping a count. If the programmer uses an array
  19520. object, indexing is available.
  19521. Another example of this is the use of a complex number class. The operation (2
  19522. + 0i) * (6 + 0i) can be calculated much easier as 2 * 6. Because the more
  19523. general form is used, the optimization for a special case is missed. The
  19524. ability to generalize is a major plus for C++, but can limit optimization.
  19525.  
  19526.  
  19527. Code Bloat
  19528.  
  19529.  
  19530. Another issue I deal with here is code bloat. I used to think a program was
  19531. big if I had to leave small model (greater than 64 kilobytes of code or data).
  19532. Our current application is about 1.5 megabytes and growing (from about 30,000
  19533. lines of code and two libraries). Part of this growth stems from the tendency
  19534. to make objects all-purpose (or worse, multi-purpose). Poorly designed objects
  19535. often give liberal access to their internals, or multiple methods of access. A
  19536. well designed object should do one thing, one way, and do it well. The public
  19537. interface should be minimal in order to guide the programmer to the expected
  19538. usage, and to keep things simple.
  19539. Making matters worse, currently the linkers I use (Borland's tlink and HP-UX
  19540. 1d) always link in all virtual functions even if they are never used. This is
  19541. because all virtual functions are referenced in an object's virtual table,
  19542. which is included with a class constructor. I have heard that new linkers are
  19543. addressing this problem, but without thorough evaluation of all the code, they
  19544. will have trouble determining whether any one virtual function will assuredly
  19545. be called (or assuredly not be called).
  19546. Another culprit in code bloat is the inline function. In the C++ style of
  19547. object-oriented programming, small functions are very prevalent, and inline
  19548. functions are necessary for a well oiled program. The factor that should
  19549. decide whether a function is inline is the ratio of time spent calling the
  19550. function to the time spent inside the function. If the time spent inside the
  19551. function is much greater than the time spent calling the function, making it
  19552. inline won't help speed much and will simply contribute to excessive code
  19553. size. This implies that to use inlines effectively one should be aware of the
  19554. inner workings of the compiler, and all of the side effects caused by some C++
  19555. functions.
  19556. One of the best ways to optimize is to use a profiler. I used the Borland
  19557. Windows profiler extensively while optimizing. In spite of its tendency to
  19558. crash, or reboot my computer occasionally, it was a tremendous help not only
  19559. in optimizing, but in finding bugs and memory leaks. The Borland profiler is
  19560. interactive, much like a visual debugger. It allows you to stop the program at
  19561. any point to examine statistics collected, to turn profiling on and off during
  19562. a single run, and to selectively profile portions of the code. It provides
  19563. time spent on a single line of code, and the number of times a single line is
  19564. called. This can be very handy for finding hidden bugs (see Figure 2).
  19565. Profiling event-driven programs can be a challenge since the main loop of the
  19566. program may be inaccessible. The method I used was to profile each of the main
  19567. classes of the program individually. Since we keep all of the code for a class
  19568. in one module, that meant compiling that module with the debugging option on.
  19569. In the profiler I could then set a profile area on every line in the module.
  19570. After running the program and doing a few transactions that would exercise the
  19571. class I was profiling, the statistics window would show me where the most time
  19572. was spent. I used this technique for finding candidates for inlining and other
  19573. optimizations. By profiling the main classes first, I was able to tell which
  19574. of the sub-classes were in need of optimizing, focusing my effort where it was
  19575. needed.
  19576. There are some limitations on inlining virtual functions. If a virtual
  19577. function is called via a pointer or reference to an object, the actual
  19578. function that gets called depends on the original type of that object. The
  19579. compiler cannot determine at compile time the correct function, and therefore
  19580. it cannot inline it. Virtual functions that are called via an actual object,
  19581. or ones that are explicitly called with the :: operator, can be inlined (see
  19582. Figure 3). In at least one case we changed a virtual function to a non-virtual
  19583. function in order to take advantage of inlining. This step must be taken with
  19584. caution, however, possibly adjusting for any change in functionality.
  19585. The decision to inline doesn't need to be all or nothing for a function. There
  19586. might be a situation where a function can be split apart to facilitate
  19587. inlining. Figure 4 shows that the member function GetWidgetPointer gets called
  19588. 10,000 times, but only has to create a Widget 10 times. By splitting this
  19589. function in two, the part that gets executed 10,000 times can be inlined, and
  19590. the main part of the code can be isolated in a private member function.
  19591. Some special precautions should be taken when inlining constructors and
  19592. destructors. The compiler may add code that you didn't call explicitly. For
  19593. example with an inline constructor, the compiler will also inline for you the
  19594. setting of the virtual table pointer, calls to any base class constructors,
  19595. and calls to constructors of any data members that have constructors. Borland
  19596. C++ also adds code to check if the constructor is called on the behalf of a
  19597. new statement and optionally calls a memory allocation routine. The
  19598. destructors will do the same.
  19599. One more precaution for inlines, if the inline contains a function call, that
  19600. function call may also be inline, which may also contain an inline function,
  19601. and so on. Make sure you know what you put inline, Just as an aside, some
  19602. current compilers forget to call the destructors for local variables in inline
  19603. functions. This is important if the local variables hold memory or system
  19604. resources and can cause a memory leak. For now I wouldn't create local
  19605. variables or pass-by-value user-defined objects in an inline function.
  19606.  
  19607.  
  19608. Pointer Tricks
  19609.  
  19610.  
  19611. One of the best ways to address rampant free-store allocation is by counting
  19612. pointers. This is a simple but effective technique that can be used on any
  19613. class to speed up assignments and passing by value. It is best targeted at
  19614. utility classes that are used in many different places and cannot be isolated.
  19615. A good example is a string class. I pointed out above that a class always
  19616. allocating its own memory can lead to excessive memory allocation and copying.
  19617. But if the string class contains a pointer to an intermediate data structure
  19618. with a count, assignment becomes a simple matter of decrementing and
  19619. incrementing a counter (see Figure 5).
  19620. By adding a little smarts to the string class we can reduce the amount of work
  19621. done, but we don't have to change the external appearance of the class. It is
  19622. important to maintain the external appearance if you need to change the class,
  19623. but don't want to make changes to all modules that use the class.
  19624. One problem still remains. If you assign string1 to string2, then modify
  19625. string1, string2 will also get modified. This problem is solved by using
  19626. "copy-on-write" semantics. Copy-on-write is a strategy where any member
  19627. function of the string class that modifies the string will first call a
  19628. function that splits off a private copy of the internal implementation (see
  19629. Figure 6).
  19630. In our application we use both pointer counting and pointer counting with copy
  19631. on write. The class BMOIterator mentioned above uses pointer counting as a
  19632. smart pointer. Besides constructors, assignment operators, and a destructor,
  19633. the only member function is the overloaded operator->. An object used with
  19634. this operator appears as simply a pointer (see Figure 7). In order to work
  19635. intuitively as a pointer, we did not implement copy on write. Also, since a
  19636. BMOIterator holds system resources, we judged it best that no copying should
  19637. take place unless explicitly asked for.
  19638. In another class, AttributeList, which is basically a list of integers, we did
  19639. implement copy on write. Since these AttributeLists are not often modified,
  19640. adding copy on write doesn't add significantly to run time. However after
  19641. running the AttributeList code through the profiler, we discovered that there
  19642. were many thousands of empty lists being created, and much time being spent
  19643. allocating arrays of zero length. To alleviate this situation, we established
  19644. a special case where a zero-length list was represented by a null
  19645. implementation pointer (see Figure 8.). Copying and assignment are slightly
  19646. longer but the default constructor is simplified. Only after using the
  19647. profiler did we become aware of this special case optimization.
  19648.  
  19649.  
  19650. Overloading new and delete
  19651.  
  19652.  
  19653. Another strategy for addressing memory allocation bottlenecks is to overload
  19654. the new and delete operators for a class. Using a special purpose memory
  19655. allocation algorithm rather than the general purpose algorithm used by malloc
  19656. and free, you can squeeze out additional speed. By using a different heap for
  19657. different allocation sizes you can reduce the amount of time needed to search
  19658. for a chunk of free memory, and reduce fragmentation as well.
  19659. An example of this is presented in Listing 1 - Listing 3, which implement a
  19660. memory-pool class and a linked list class that uses the memory pool. The
  19661. memory-pool class is a heap for a specific size allocation. It is used by
  19662. associating one instance of the memory pool class with any class (via a static
  19663. data member) and overloading the new and delete operators to use the pool.
  19664. Since the specific new operator is only used for that class member, the size
  19665. is always the same.
  19666. The heap works by allocating 32 objects at a time, and maintaining a bit map
  19667. of used areas. The pool has two lists, one for chunks that have at least one
  19668. space, and the other for chunks that are completely used up. Therefore the
  19669. allocation logic has to search only one chunk for a free spot. (I have to
  19670. thank my brother Mark for the bit searching method, without which this method
  19671. was actually slower than the built-in malloc.) This special version of new
  19672. could also be rewritten in assembly language for even greater speed.
  19673. For classes that hold limited resources (such as our BMOJoin which controls
  19674. one or more network sockets), a good strategy is to delay creation until the
  19675. last possible moment. We used code similar to that in Figure 4b inside the
  19676. BMOIterator, calling GetJoinPointer rather than accessing the BMOJoin pointer
  19677. itself. In this way we can create BMOIterators as we put up a window, but the
  19678. BMOJoin isn't created until a search is performed. We added a member function
  19679. called Disconnect to the BMOIterator, and call it when a window is put into
  19680. the background to delete the BMOJoin pointer and free resources for the
  19681. foreground window (see Figure 9).
  19682.  
  19683.  
  19684.  
  19685. Conclusion
  19686.  
  19687.  
  19688. Finally, running the profiler through a particularly sluggish area, we
  19689. discovered that a lot of time was being spent padding strings with spaces. The
  19690. problem here was that operator+= for the string was not as efficient as we had
  19691. hoped. By going to a lower level, and accessing C-style strings directly, we
  19692. managed to speed up the padding process considerably.
  19693. I should mention that a good string class would have had a padding function,
  19694. or at least the ability to create a blank string of count bytes. This
  19695. low-level access then wouldn't be necessary. Since the string class was part
  19696. of a commercial library we didn't want to modify or add to it. It's good to
  19697. know that all the old C tricks are available even if just used at the lowest
  19698. levels. Credit has to be given here to the profiler for finding this
  19699. bottleneck.
  19700. Many of the optimizations described above are fairly simple because of the
  19701. separation of interface from implementation. The idea is to be able to grease
  19702. up a class without having to worry about side effects that might be possible
  19703. in a less restrictive interface. Other optimizations are possible by dipping
  19704. into C++'s C heritage as a low-level language.
  19705. Of course there is no substitute for a good design effort up front. A
  19706. temptation in design is to make a lot of "friendly" classes, which access each
  19707. other's internals. But what you wind up with is "spaghetti objects." By
  19708. maintaining integrity between classes, you can twiddle with the internal bits
  19709. to your heart's content without having to worry about unforeseen side effects.
  19710. Figure 1 Good objects must do their own memory management
  19711. class BadString {
  19712. public:
  19713. // Using the pointer directly:
  19714.  BadString(char *s) { ptr = s; }
  19715. // Where did pointer come from:
  19716.  ~BadString() { delete[] ptr; }
  19717. private:
  19718.  char *ptr;
  19719. };
  19720.  
  19721. char *strdup(const char *str)
  19722. {
  19723.  return strcpy(new
  19724.  char[strlen(str)+1],str);
  19725. }
  19726.  
  19727. class GoodString {
  19728. public:
  19729. // Don't use the pointer, copy it:
  19730.  GoodString(const char *s)
  19731.  { ptr= strdup(s); }
  19732. // Now you can safely delete it:
  19733.  ~GoodString()
  19734.  { delete[] ptr; }
  19735. private:
  19736.  char *ptr;
  19737. };
  19738.  
  19739. // End of File
  19740. Figure 2 Using the profiler to find hidden problems
  19741. // Why isn't the while loop
  19742. // ever executed?
  19743.  
  19744. time count
  19745. 0.0012 50 int flag = 0;
  19746. 0.0031 50 while(flag = 0)
  19747.  {
  19748. 0.0000 0 if(doTilTrue())
  19749. 0.0000 0 flag = 1;
  19750.  }
  19751.  
  19752. // End of File
  19753. Figure 3 Inlining virtual functions
  19754. class Base {
  19755. public:
  19756.  virtual void print()
  19757.  { printf("Base"); }
  19758. };
  19759.  
  19760.  
  19761. class Derived1 : public Base {
  19762. public:
  19763.  virtual void print()
  19764.  {
  19765. // Base::print() can be inlined
  19766.  Base::print();
  19767.  printf("Derived1");
  19768.  }
  19769. };
  19770.  
  19771. class Derived2 : public Base {
  19772. public:
  19773.  virtual void print()
  19774.  {
  19775. // Base::print() can be inlined
  19776.  Base::print();
  19777.  printf("Derived2");
  19778.  }
  19779. };
  19780.  
  19781. void function(Base *bp)
  19782. {
  19783. // Doesn't know whether
  19784. // to call Derived1::print()
  19785. // or Derived2::print(),
  19786. // won't inline:
  19787.  bp->print();
  19788.  
  19789. // Forced to call Base::print(),
  19790. // can inline:
  19791.  bp->Base::print();
  19792.  Derived1 d1o;
  19793. // Forced to call
  19794. // Derived1::print(), can
  19795. // inline:
  19796.  d1o.print();
  19797. }
  19798.  
  19799. // End of File
  19800. Figure 4 Efficient inlining
  19801. Figure 4a. Splitting functions to facilitate inlining.
  19802.  
  19803. 5.1274 10000 Widget *WidgetHome::GetWidgetPointer()
  19804.  {
  19805. 1.0333 10000 if(widgetPtr == NULL)
  19806.  {
  19807. 0.0012 10 widgetPtr = new Widget;
  19808. 0.0013 10 if(widgetPtr == NULL 
  19809.  widgetPtr->isError())
  19810. 0.0000 0 return NULL;
  19811.  }
  19812. 1.0237 10000 return widgetPtr;
  19813. 3.5429 10000 }
  19814.  
  19815. Figure 4b. Inlining only the part called most often.
  19816.  
  19817.  // the inline doesn't show up on the profile count:
  19818.  
  19819.  inline Widget *WidgetHome::GetWidgetPointer()
  19820.  
  19821.  {
  19822.  return widgetPtr ?
  19823.  widgetPtr:
  19824.  PrivateGetWidgetPointer();
  19825.  }
  19826.  
  19827. 0.0020 10 Widget *WidgetHome::PrivateGetWidgetPointer();
  19828.  {
  19829. 0.0059 10 widgetPtr = new Widget;
  19830. 0.0035 10 if(widgetPtr == NULL 
  19831.  widgetPtr->isError())
  19832. 0.0000 0 return NULL;
  19833. 0.0008 10 return widgetPtr;
  19834. 0.0001 10 }
  19835.  
  19836. // End of File
  19837. Figure 5 Counting pointers
  19838. class String {
  19839. private:
  19840.  struct SringImp {
  19841.  char *ptr;
  19842.  unsigned count;
  19843.  StringImp(const char *str)
  19844.  : ptr(strdup(str)),
  19845.  count(l)
  19846.  {
  19847.  ;
  19848.  }
  19849.  ~StringImp()
  19850.  {
  19851.  delete[] ptr;
  19852.  }
  19853.  } *imp;
  19854. public:
  19855.  String(const char *str)
  19856.  {
  19857.  imp = new StringImp(str);
  19858.  }
  19859.  String(const String &str)
  19860.  {
  19861.  imp = str.imp;
  19862.  imp->count++;
  19863.  }
  19864. // assignment is a little tricky
  19865.  String &
  19866.  operator=(const String &str)
  19867.  {
  19868. // increment first in case
  19869. // of assignment to self
  19870.  str.imp->count++;
  19871. // be sure to clean up the old imp!
  19872.  if(--imp->count == 0)
  19873.  delete imp;
  19874.  imp = str.imp;
  19875.  return *this;
  19876. }
  19877. ~String()
  19878. {
  19879.  if(--imp->count == 0)
  19880.  
  19881.  delete imp;
  19882.  }
  19883. };
  19884. // End of File
  19885. Figure 6 Implementing copy on write
  19886. class String {
  19887.  
  19888. // the contents of the string
  19889. // class from Figure 5.
  19890.  
  19891. ...
  19892.  
  19893. // this indexing operator could
  19894. // modify the string
  19895.  char &operator[](int i)
  19896.  {
  19897.  if(imp->count > 1)
  19898.  Split();
  19899.  return imp->ptr[i];
  19900.  }
  19901. // This indexing operator won't
  19902. // modify, don't split.
  19903.  char operator[](int i) const
  19904.  {
  19905.  return imp->ptr[i];
  19906.  }
  19907. private:
  19908.  void Split()
  19909.  {
  19910. // Create private copy:
  19911.  imp->count--;
  19912.  imp = new
  19913.  StringImp(imp->ptr);
  19914.  }
  19915. };
  19916. // End of File
  19917. Figure 7 Implementing smart pointers
  19918. class BMOIteratorImp;
  19919.  
  19920. class BMOIterator {
  19921. public:
  19922.  BMOIterator(const char *dbname);
  19923.  BMOIterator(const BMOIterator &);
  19924.  BMOIterator &
  19925.  operator=(const BMOIterator &);
  19926.  ~BMOIterator();
  19927.  BMOIteratorImp *operator->()
  19928.  {
  19929.  return imp;
  19930.  }
  19931. private:
  19932.  BMOIteratorImp *imp;
  19933. }
  19934.  
  19935. function()
  19936. {
  19937.  BMOIterator it("DBNAME");
  19938. // AddCol is a memeber of
  19939. // BMOIteratorImp
  19940.  
  19941.  it->AddCol("table.col1");
  19942. }
  19943. // End of File
  19944. Figure 8 Using a special case for an empty list
  19945. class AttributeList {
  19946. private:
  19947.  struct AttributeListImp {
  19948.  int *list;
  19949.  unsigned count;
  19950.  AttributeListImp (int *
  19951.  data, unsigned size)
  19952.  {
  19953.  list = new int[size];
  19954.  memcpy(list,data,
  19955.  sizeof(int)*size);
  19956.  count = 1;
  19957.  }
  19958.  } *ptr;
  19959. public:
  19960.  AttributeList()
  19961.  {
  19962. // Empty list special case:
  19963.  ptr = NULL;
  19964.  }
  19965.  AttributeList(int *data,
  19966.  unsigned size)
  19967.  {
  19968.  ptr = new
  19969.  AttributeListImp(data,
  19970.  size);
  19971.  }
  19972.  AttributeList(const
  19973.  AttributeList &a)
  19974.  {
  19975. // Now we have to check
  19976. // for null:
  19977.  ptr: a.ptr;
  19978.  if(ptr) ptr->count++;
  19979.  }
  19980.  
  19981. ...
  19982.  
  19983. };
  19984.  
  19985. // End of File
  19986. Figure 9 Delaying creation of an object by restricting access
  19987. class BMOJoin;
  19988.  
  19989. class BMOIteratorImp {
  19990. public:
  19991.  BMOIteratorImp()
  19992.  {
  19993.  itsJoinPtr = NULL;
  19994.  }
  19995.  ~BMOIteratorImp()
  19996.  {
  19997.  delete itsJoinPtr;
  19998.  }
  19999.  int Search(char *str)
  20000.  
  20001.  {
  20002.  return
  20003.  GetJoinPointer()->Search(str);
  20004.  }
  20005.  void Disconnect()
  20006.  {
  20007.  delete itsJoinPtr;
  20008.  itsJoinPtr = NULL;
  20009.  }
  20010. private:
  20011. // this is actually split apart
  20012. // (see Figure 4.)
  20013.  BMOJoin *GetJoinPointer()
  20014.  {
  20015.  if(itsJoinPtr)
  20016.  return itsJoinPtr;
  20017.  itsJoinPtr = new
  20018.  BMOJoin(itsStoredParameters);
  20019.  if(itsJoinPtr == NULL)
  20020. // for now throw is just an inline
  20021. // for exit()
  20022.  throw(ErrNoMem);
  20023.  return itsJoinPtr;
  20024.  }
  20025. };
  20026.  
  20027. // End of File
  20028. Figure 10 Going low-level to improve efficiency
  20029. Figure 10a. Results of an inefficient += operator.
  20030.  
  20031.  1.0235 500 void PadWithSpaces(CString &str, int count)
  20032.  {
  20033.  5.0346 20000 while(count--)
  20034. 30.0397 19000 str += " ";
  20035.  1.4354 500 }
  20036.  
  20037. Figure 10b. Improved performance with low-level C-strings.
  20038.  
  20039.  1.0235 500 void PadWithSpaces(CString &str, int count)
  20040.  {
  20041.  2.4505 500 char *tmp = new char[count+l];
  20042.  3.2930 500 memset(tmp,' ',count);
  20043.  0.9438 500 tmp[count] = '\0';
  20044.  5.3049 500 str += tmp;
  20045.  1.9430 500 delete[] tmp;
  20046.  1.4353 500 }
  20047.  
  20048. // End of File
  20049.  
  20050. Listing 1 Defines class MemoryPool
  20051. #ifndef MEMPOOL_H
  20052. #define MEMPOOL_H
  20053.  
  20054. #include <stddef.h>
  20055.  
  20056. const CharSize = 8;
  20057. const PoolSize =
  20058. sizeof(unsigned long)*CharSize;
  20059.  
  20060.  
  20061. class MemoryPoolLink {
  20062. private:
  20063. friend class MemoryPool;
  20064. MemoryPoolLink(size_t _size,
  20065. MemoryPoolLink *_next);
  20066. ~MemoryPoolLink();
  20067. void *malloc(size_t size);
  20068. void free(void *, size_t size);
  20069. unsigned long bits;
  20070. MemoryPoolLink *next;
  20071. char *data;
  20072. };
  20073.  
  20074. class MemoryPool {
  20075. MemoryPoolLink freeHead;
  20076. MemoryPoolLink usedHead;
  20077. size_t size;
  20078. public:
  20079. MemoryPool(size_t size);
  20080. void *add();
  20081. void *malloc();
  20082. void free(void *);
  20083. };
  20084.  
  20085. #endif
  20086.  
  20087.  
  20088. Listing 2 Memory pool member functions
  20089. #include "mempool.h"
  20090.  
  20091. MemoryPoolLink::
  20092. MemoryPoolLink(
  20093. size_t size,
  20094. MemoryPoolLink *_next)
  20095. : bits(0l),
  20096. data(new
  20097. char[size*PoolSize]),
  20098. next(_next)
  20099. {
  20100. ;
  20101. }
  20102.  
  20103. MemoryPoolLink::
  20104. ~MemoryPoolLink()
  20105. {
  20106. delete[] data;
  20107. }
  20108. void *MemoryPoolLink::malloc(
  20109. size_t size)
  20110. {
  20111. static char lookup[] = {
  20112. 0, 1, 0, 2,
  20113. 0, 1, 0, 3,
  20114. 0, 1, 0, 2,
  20115. 0, 1, 0, 4,
  20116. };
  20117. int shift = 0;
  20118. unsigned long b = bits;
  20119. if((b&0xFFFF) == 0xFFFF)
  20120.  
  20121. {
  20122. shift = 16;
  20123. b >>= 16;
  20124. }
  20125. if((b&0xFF) == 0xFF)
  20126. {
  20127. shift += 8;
  20128. b >>= 8;
  20129. }
  20130. if((b&0xF) == 0xF)
  20131. {
  20132. shift += 4;
  20133. b >>= 4;
  20134. }
  20135. shift += lookup[b&0xF];
  20136. bits = (1l << shift);
  20137. return data +
  20138. (shift * size);
  20139. }
  20140.  
  20141. void MemoryPoolLink::free(
  20142. void *ptr,
  20143. size_t size)
  20144. {
  20145. bits &=
  20146. ~(1l <<
  20147. ((char *)ptr-data)/size);
  20148. }
  20149.  
  20150. MemoryPool::MemoryPool(
  20151. size_t_size)
  20152. : size(_size),
  20153. freeHead(0,NULL),
  20154. usedHead(0,NULL)
  20155. {
  20156. }
  20157.  
  20158. void *MemoryPool::add()
  20159. {
  20160. MemoryPoolLink *temp = new
  20161. MemoryPoolLink(size,
  20162. freeHead.next);
  20163. if(temp == NULL)
  20164. return NULL;
  20165. freeHead.next = temp;
  20166. return
  20167. freeHead.next->
  20168. malloc(size);
  20169. }
  20170.  
  20171. void *MemoryPool::malloc()
  20172. {
  20173. if(freeHead.next)
  20174. {
  20175. void *ret =
  20176. freeHead.next->
  20177. malloc(size);
  20178. if(freeHead.next->bits ==
  20179. 0xFFFFFFFFL)
  20180.  
  20181. {
  20182. MemoryPoolLink *temp =
  20183. freeHead.next;
  20184. freeHead.next =
  20185. temp->next;
  20186. temp->next =
  20187. usedHead.next;
  20188. usedHead.next = temp;
  20189. }
  20190. return ret;
  20191. }
  20192. return add();
  20193. }
  20194.  
  20195. void MemoryPool::free(
  20196. void *ptr)
  20197. {
  20198. MemoryPoolLink *temp =
  20199. freeHead.next;
  20200. MemoryPoolLink *prev =
  20201. &freeHead;
  20202. while(temp)
  20203. {
  20204. ptrdiff_t diff =
  20205. temp->data -
  20206. (char *)ptr;
  20207. if(diff > 0 &&
  20208. diff < size*PoolSize)
  20209. {
  20210. temp->free(ptr, size);
  20211. if(temp->bits == 01)
  20212. {
  20213. prev->next =
  20214. temp->next;
  20215. delete temp;
  20216. }
  20217. return;
  20218. }
  20219. prev = temp;
  20220. temp = temp->next;
  20221. }
  20222. temp = usedHead.next;
  20223. prev = &usedHead;
  20224. while(temp)
  20225. {
  20226. ptrdiff_t diff =
  20227. temp->data -
  20228. (char *)ptr;
  20229. if(diff > 0 &&
  20230. diff < size*PoolSize)
  20231. {
  20232. temp->free(ptr, size);
  20233. prev->next =
  20234. temp->next;
  20235. temp->next =
  20236. freeHead.next;
  20237. freeHead.next =
  20238. temp;
  20239. return;
  20240.  
  20241. }
  20242. prev = temp;
  20243. temp = temp->next;
  20244. }
  20245. }
  20246.  
  20247. // End of File
  20248.  
  20249.  
  20250. Listing 3 A linked list for the memory pool
  20251. #include "mempool.h"
  20252. #include <fstream.h>
  20253. #include <string.h>
  20254.  
  20255. class Link {
  20256. char *data;
  20257. Link *next;
  20258. static MemoryPool pool;
  20259. public:
  20260. friend class List;
  20261. friend class Iterator;
  20262. Link(const char *_data,
  20263. Link *_next)
  20264. {
  20265. next =_next;
  20266. data = strcpy(new
  20267. char[strlen(_data)+1],
  20268. _data);
  20269. }
  20270. ~Link()
  20271. {
  20272. delete data;
  20273. }
  20274. void *
  20275. operator new(size_t)
  20276. {
  20277. return pool.malloc();
  20278. }
  20279. void
  20280. operator delete(void *ptr)
  20281. {
  20282. pool.free(ptr);
  20283. }
  20284. };
  20285.  
  20286. MemoryPool
  20287. Link::pool(sizeof(Link));
  20288.  
  20289. class List {
  20290. Link head;
  20291. public:
  20292. friend class Iterator;
  20293. List ();
  20294. -List();
  20295. void add(char *data);
  20296. };
  20297.  
  20298. class Iterator {
  20299. Link *link;
  20300.  
  20301. public:
  20302. Iterator(List &_list)
  20303. {
  20304. link = _list.head.next;
  20305. }
  20306. char *Next()
  20307. {
  20308. char *ret = link ?
  20309. link->data : NULL;
  20310. link = link->next;
  20311. return ret;
  20312. }
  20313. };
  20314.  
  20315. List::List()
  20316. : head("",NULL)
  20317. {
  20318. ;
  20319. }
  20320.  
  20321. List::~List()
  20322. {
  20323. while(head.next)
  20324. {
  20325. Link *temp =
  20326. head.next->next;
  20327. delete head.next;
  20328. head.next = temp;
  20329. }
  20330. }
  20331.  
  20332. void List::add(char *data)
  20333. {
  20334. head.next = new
  20335. Link(data,head.next);
  20336. }
  20337.  
  20338. main(int, char **argv)
  20339. {
  20340. static char buff[1024];
  20341. List list;
  20342. ifstream in(argv[1]);
  20343. while(in.getline(buff,
  20344. sizeof(buff)))
  20345. list.add(buff);
  20346. Iterator it(list);
  20347. char *ptr;
  20348. while((ptr = it.Next()) !=
  20349. NULL)
  20350. cout << ptr << endl;
  20351. }
  20352.  
  20353. // End of File
  20354.  
  20355.  
  20356.  
  20357.  
  20358.  
  20359.  
  20360.  
  20361.  
  20362.  
  20363.  
  20364.  
  20365.  
  20366.  
  20367.  
  20368.  
  20369.  
  20370.  
  20371.  
  20372.  
  20373.  
  20374.  
  20375.  
  20376.  
  20377.  
  20378.  
  20379.  
  20380.  
  20381.  
  20382.  
  20383.  
  20384.  
  20385.  
  20386.  
  20387.  
  20388.  
  20389.  
  20390.  
  20391.  
  20392.  
  20393.  
  20394.  
  20395.  
  20396.  
  20397.  
  20398.  
  20399.  
  20400.  
  20401.  
  20402.  
  20403.  
  20404.  
  20405.  
  20406.  
  20407.  
  20408.  
  20409.  
  20410.  
  20411.  
  20412.  
  20413.  
  20414.  
  20415.  
  20416.  
  20417.  
  20418.  
  20419.  
  20420.  
  20421.  
  20422.  
  20423.  
  20424. Enhancing the UNIX Korn Shell Using Predictor Techniques
  20425.  
  20426.  
  20427. Philip Thomas and Shmuel Rotenstreich
  20428.  
  20429.  
  20430. Philip K. Thomas received the B.S. degree in Physics from the California
  20431. Polytechnic State University and is currently in the process of defending his
  20432. Ph.D. dissertation in Computer Science at the George Washington University.
  20433. Philip also serves as Director of System Architecture at PRC corporation in
  20434. Mclean, Virginia and can be reached at philip@rsi.prc.com.
  20435.  
  20436.  
  20437. Shmuel Rotenstreich received the B.S. degree in Computer Science from the Tel
  20438. Aviv University and the Ph.D. degree also in Computer Science from the
  20439. University of California at San Diego. Shmuel is currently Professor of
  20440. Engineering and Applied Sciences at the George Washington University and can
  20441. be reached at shmuel@sparko.gwu.edu.
  20442.  
  20443.  
  20444.  
  20445.  
  20446. Introduction
  20447.  
  20448.  
  20449. On most computer systems, users interact with the system through
  20450. command-language interpreters called shells. These shells accept user input
  20451. and interpret them as commands for the system. There are three major UNIX
  20452. shells: Bourne, C and Korn (kshell). Other shells include command.com for
  20453. MS-DOS and for OS/2.
  20454. The Korn Shell, which we consider here, offers sophisticated management of
  20455. past commands (history). We have enhanced this functionality to include a
  20456. learning automaton that predicts the next command. This command prediction
  20457. often allows the user to avoid entering the command sequence in its entirety.
  20458. In most of the computing environments we considered, this enhanced shell
  20459. predicted the next command with a high degree of accuracy.
  20460. This article describes our shell enhancements and details some of the methods
  20461. we used to implement them. Although the Korn Shell was our target shell, we
  20462. have applied these same enhancements to the C Shell. We believe this article
  20463. will show that our enhancements are applicable to other shells as well.
  20464.  
  20465.  
  20466. Attaining Command Cycle Consciousness
  20467.  
  20468.  
  20469. In our research we have found that most users exhibit cyclic behavior when
  20470. interacting with an operating system. For example, to create and execute a
  20471. program, users may produce the following:
  20472. 1. Command: Evoke editor -- create the source file.
  20473. 2. Command: Create executable -- compile & link the source file.
  20474. 3. Command: Execute the file created in (2) -- examine the results.
  20475. The user typically repeats this sequence until he or she has completed and
  20476. thoroughly debugged the program.
  20477. If a shell can recognize such cyclical behavior (we say it has cycle
  20478. consciousness) it can predict the user's next command at any point in the
  20479. sequence. We incorporate cycle consciousness into the shell by integrating it
  20480. with the Predictor module outlined in the next section.
  20481.  
  20482.  
  20483. The Predictor Module
  20484.  
  20485.  
  20486. To incorporate cycle-based knowledge into various operating system facilities,
  20487. we have developed an abstract data type module called a Predictor (Listing 1).
  20488. The Predictor is a learning automation representative of the
  20489. learning-from-analogy class of learning strategies. In learning-from-analogy
  20490. strategies, the learner reaches a conclusion about the current case by
  20491. considering previously processed cases bearing strong similarities to the
  20492. current case. For the predictor module, the previously processed cases are the
  20493. sequences of commands already encountered from the command stream.
  20494. The Predictor module incorporates an abstract component as well as several
  20495. concrete components. The abstract component is the prediction algorithm, which
  20496. we describe shortly. The concrete components consist of three major classes:
  20497. 1. Control Class: The two instances of this class supply entry points for
  20498. initializing and terminating the Predictor Module.
  20499. 2. Input Class: This class obtains the next command from external modules.
  20500. 3. Predict Class: This class makes predictions for the Predictor module.
  20501. predictor can make two types of predictions:
  20502. 1) The next command
  20503. 2) When a particular command will next occur
  20504. By creating a predictor class, we encapsulate all cycle-dependent code and
  20505. data, making it easier to modify. This method of encapsulation also permits
  20506. the coexistence of multiple predictors. (Environments where multiple command
  20507. streams exist require multiple predictors.) We describe the predictor class in
  20508. detail later in this article.
  20509.  
  20510.  
  20511. The Algorithm
  20512.  
  20513.  
  20514. For this discussion we say that a command stream C is a sequence of N commands
  20515. composed of M unique commands. These commands arrive at the predictor one at a
  20516. time via the input interface. These commands determine the current state of
  20517. the cycle (command stream).
  20518. The predictor module maintains a weighted adjacency matrix A. A is an M x M
  20519. matrix which represents a command stream C (this command stream dynamically
  20520. increases in length as each subsequent command arrives), where the ijth
  20521. element A[i,j] is 0 if command j never follows command i, or some positive
  20522. integer indicating the number of times command j follows command i. (Some
  20523. readers may recogonize this implementation as part of a semantic network.)
  20524. To predict the next command, the algorithm locates the corresponding row for
  20525. the latest command n in the adjacency matrix A. The algorithm then selects
  20526. command i such that command i is: MAX(A[n,i]) i=1,2..M (index of command i)
  20527. and A[n,i] >1
  20528. If such a command exists, the algorithm returns command i as the best
  20529. prediction of the next command to occur. If no such command exists, the
  20530. algorithm returns indicating that the predictor module cannot predict the next
  20531. command given its current state.
  20532. The algorithm performs the following steps to predict when a particular
  20533. command will next occur:
  20534. 1. Save the current state of the predictor module.
  20535. 2. Set count=0.
  20536. 3. Call the predict-the-next-command interface repeatedly until the return
  20537. value command i is
  20538.  
  20539. a) Equal to the specified command
  20540. b) A value indicating that the predictor cannot make a prediction
  20541. c) A value previously returned by the predict-the-next-command interface (this
  20542. implies that the predictor assumes the command stream will cycle without an
  20543. occurrence of the specified command). For each time the
  20544. predict-the-next-command interface is called, increment count.
  20545. 4. Restore the original state of the predictor module.
  20546. 5. Return count if step 3 terminated because of condition 3.a, otherwise
  20547. return a value indicating that the predictor cannot predicting when the
  20548. specified command will next occur.
  20549.  
  20550.  
  20551. The Predictor Class
  20552.  
  20553.  
  20554. The predictor class contains three publicly accessible functions to perform
  20555. input and output:
  20556. PutNextCommand -- This function registers commands occurring on the system. We
  20557. modified the main control loop of the kshell to call this function whenever
  20558. the kshell assembled a command and registered it to provide "history"
  20559. functionality. This function returns values corresponding to internal fatal
  20560. errors and success.
  20561. GetNextCommand -- This function passes the next predicted command to the
  20562. system, if it can be predicted. We augmented the kshell source to recognize
  20563. when the user types ESC-p (in emacs mode) at the prompt, and to call
  20564. GetNextCommand. As a result, the kshell produces either a beep (to indicate
  20565. the inability to predict the next command) or a command string adjacent to the
  20566. prompt, which the user may execute by typing a carriage return.
  20567. WhenNextCommand -- As its name implies, this function predicts when a
  20568. particular command will occur. We do not use this function in the kshell. We
  20569. have included it in the predictor module because it is invaluable in other
  20570. systems that require this kind of prediction. (e.g. in a file caching system
  20571. -- when a file needs "flushing," information about when a file will next be
  20572. used is advantageous.) This function returns zero to indicate that the command
  20573. stream will cycle before it encounters the specified command. This function
  20574. returns a value of less than zero to signal an internal error.
  20575. These last two functions maintain and manipulate the adjacency matrix
  20576. mentioned previously.
  20577.  
  20578.  
  20579. Results
  20580.  
  20581.  
  20582. To show how well the predictor works, we have derived the following examples
  20583. from a command stream in a programming environment. Figure 1 shows the first
  20584. example. The first column shows the actual command stream. The second column,
  20585. with the prefix p: shows the predicted command stream. The last two columns
  20586. show the current percentile of successful predictions and the maximum
  20587. percentile of successful predictions, respectively. The second example (Figure
  20588. 2) differs from the first only by one command; we have removed this command,
  20589. ls, from the stream in example 2. This command represents an "exception" to
  20590. the cyclical nature of the command stream. Thus, example 2 shows the effect of
  20591. filtering out exceptions.
  20592.  
  20593.  
  20594. Open Issues:
  20595.  
  20596.  
  20597. To keep this article consice several items have not been sufficiently
  20598. developed. The following are a few points that we believe require further
  20599. clarification.
  20600.  
  20601.  
  20602. How are command parameters handled?
  20603.  
  20604.  
  20605. Commands can be classified as either tightly coupled or loosely coupled with
  20606. their parameters. The degree of coupling is context dependent. For instance,
  20607. when the predictor is used in a file caching subsystem, predicting the
  20608. sequence of parameters (e.g. file names) is more important than predicting the
  20609. sequence of the commands themselves. We say the commands are loosely coupled.
  20610. In the case of tightly coupled commands, we treat parameters and their
  20611. commands as one predictor unit, that is, the predictor considers the same
  20612. command with different parameters as distinct and dissimilar commands. (The
  20613. predictor outlined in this article works this way.) In the loosely coupled
  20614. scenario, we either parse the parameters out and use them as distinct
  20615. predictor units, or, as is usually more beneficial, we still treat
  20616. command-parameter sets as tightly coupled and treat them as single predictor
  20617. units.
  20618.  
  20619.  
  20620. Are command cycles common?
  20621.  
  20622.  
  20623. Yes. When we remove a lot of the "noise" from command streams, we find that
  20624. most of the commands are members of command cycles. The "noise," we have
  20625. discovered, comes in many forms. Many commands streams are sprinkled with
  20626. commands we isolate as "exceptions." These exceptions would be commands like
  20627. ls, dir, who etc. Users tend to use commands like these with enough randomness
  20628. that it is easy to filter these commands out of otherwise perfectly healthy
  20629. command streams. Another form of noise is the partial cycle. The partial cycle
  20630. is best illustrated using the example session. In the edit-compile-execute
  20631. cycle mentioned above, evoking the compiler sometimes results in errors that
  20632. require the user to re-visit the editor without completing the cycle. Partial
  20633. cycles are much harder to deal with than exceptions and, consequently, our
  20634. success rate with these tends to be lower.
  20635.  
  20636.  
  20637. GUI Environments
  20638.  
  20639.  
  20640. We have also begun investigations into incorporating cycle consciousness into
  20641. graphical user interface (GUI) environments. The essential problem here is
  20642. defining a command primitive. In a command-oriented interface a command
  20643. primitve is well defined, but in a modern GUI a command primitive is more
  20644. loosely defined and may incorporate several user actions, such as mouse moves
  20645. and button clicks. User activity is hard to partition into distinct commands.
  20646.  
  20647.  
  20648. Conclusion
  20649.  
  20650.  
  20651. As is any article of this nature, we had to summarize a large body of work
  20652. both theoretical and experimental to present here. The algorithm presented
  20653. here is a first-degree learning automation. We have added several filters and
  20654. general enhancements to this automation resulting in several different
  20655. predictors that, depending on the situation, are selected and evoked at run
  20656. time (late binding -- virtual functions). Our work leveraged the predictor
  20657. across several operating-system elements including memory management,
  20658. schedulings and disk allocation. Overall, we think our assertions for
  20659. incorporating cycle consciousness have been well founded. We have had
  20660. favorable results in the areas we have studied.
  20661. References:
  20662. Peterson, James L.; Silberschatz, Abraham, Operating System Concepts,
  20663. Addison-Wesley Publishing Co, 1985.
  20664. Tanimoto, Steven L., The Elements of Artificial Intelligence, Computer Science
  20665. Press, 1990.
  20666. Thomas, Philip K., Rotenstreich, Shmuel, "Command Cycles," Ph. D.
  20667. Dissertation, George Washington University, 1993.
  20668. Anatomy of a Command Shell
  20669. In most systems, user programs and system programs are executed by the
  20670. command-language interpreter. In more sophisticated systems such as UNIX,
  20671. there are no major distinctions between this interpreter and any other program
  20672. -- so users can easily create their own shells.
  20673. The shell actually executes a command by completing a fork, after which the
  20674. child process executes an execve (load and execute) of the command. The parent
  20675. process (the shell) does a wait and suspends its own execution until the child
  20676. process finishes executing and performs an exit. In multi-tasking systems,
  20677. such as UNIX, both the shell and the command it is processing can execute
  20678. concurrently. Users can initiate concurrent execution by typing a command
  20679. followed by an ampersand. The shell interprets this symbol as an indication
  20680. not to perform the wait; instead the shell continues with the next step in its
  20681. command input by prompting the user for the next command.
  20682. This capability implies the existence of a fairly sophisticated interprocess
  20683. communication system. As a bare minimum, a child process needs a method to
  20684. signal its termination to the parent process (the shell), which is not
  20685. necessarily polling for this signal.
  20686.  
  20687. Patching the Korn Shell
  20688. The kshell source we started with was PD KornShell written by Eric Gisin
  20689. <egisin@math. UWaterloo. EDU>. PD Korn Shell installs on 4.2+ BSD System V,
  20690. and POSIX-compatible systems. PD KornShell assumes you have Standard C (ANSI)
  20691. and POSIX header files functions. The PD KornShell source is available on
  20692. several Internet locations including:
  20693. ftp.uu.net (192.48.96.9)
  20694. /usenet/comp.sources.amiga/volume91/shells and
  20695. softu1.ncu.edu.tw (140.115.19.11)
  20696. /pub5/tarz
  20697. We assembled the kshell on a HP-UX machine using HP's C++ compiler. On the
  20698. HP-UX system, which is a hybrid UNIX system (System V & BSD), only a
  20699. negligible amount of source modification was to get the shell up and running.
  20700. Since the source was in " public-domain UNIX-type" C, we decided to leave the
  20701. original parts in C and incorporate our enhancements in a C++ class. To
  20702. facilitate the intermingling of C and C++, we instructed the C++ compiler to
  20703. suppress name mangling (See "Using C/C++ with Clipper" by Mark W. Schumann in
  20704. the December 1993 issue of CUJ for a thorough treatment of name mangling in
  20705. C++).
  20706. Figure 1 A sample session with predicted and actual commands streams
  20707. Example 1:
  20708. ==========
  20709. Original Command Stream Predicted Command Stream
  20710. ----------------------- ------------------------ --- ---
  20711. o:cc -o timer timer.c p: 00% 00%
  20712. o:timer p: 00% 00%
  20713. o:ls -l timer p: 00% 00%
  20714. ***Removed from example 2****
  20715. o:vi timer.c p: 00% 00%
  20716. o:cc -o timer timer.c p:cc -o timer timer.c *16% 16%
  20717. o:timer p:timer *28% 28%
  20718. o:vi timer.c p:ls -l timer 25% 28%
  20719. o:vi tstin p:cc -o timer timer.c 22% 28%
  20720. o:cc -o timer timer.c p: 20% 28%
  20721. o:vi tstin p:timer 18% 28%
  20722. o:vi timer.c p:cc -o timer timer.c 16% 28%
  20723. o:cc -o timer timer.c p:cc -o timer timer.c *23% 28%
  20724. o:timer p:timer *28% 28%
  20725. o:vi timer.c p:vi timer.c *33% 33%
  20726. o:cc -o timer timer.c p:cc -o timer timer.c *37% 37%
  20727. o:timer p:timer *41% 41%
  20728. o:vi timer.c p:vi timer.c *44% 44%
  20729. o:cc -o timer timer.c p:cc -o timer timer.c *47% 47%
  20730. o:timer p:timer *50% 50%
  20731. o:vi timer.c p:vi timer.c *52% 52%
  20732. o:cc -o timer timer.c p:cc -o timer timer.c *54% 54%
  20733. o:timer p:timer *56% 56%
  20734. o:vi timer.c p:vi timer.c *58% 58%
  20735. o:cc -o timer timer.c p:cc -o timer timer.c *60% 60%
  20736. o:timer p:timer *61% 61%
  20737. o:vi timer.c p:vi timer.c *62% 62%
  20738. o:cc -o timer timer.c p:cc -o timer timer.c *64% 64%
  20739. o:timer p:timer *65% 65%
  20740. o:vi timer.c p:vi timer.c *66% 66%
  20741. o:cc -o timer timer.c p:cc -o timer timer.c *67% 67%
  20742. o:timer p:timer *68% 68%
  20743. o:vi timer.c p:vi timer.c *69% 69%
  20744. o:cc -o timer timer.c p:cc -o timer timer.c *70% 70%
  20745. o:timer p:timer *71% 71%
  20746. o:vi timer.c p:vi timer.c *72% 72%
  20747. o:cc -o timer timer.c p:cc -o timer timer.c *72% 72%
  20748. o:timer p:timer *73% 73%
  20749. o:vi timer.c p:vi timer.c *74% 74%
  20750. o:cc -o timer timer.c p:cc -o timer timer.c *75% 75%
  20751. o:timer p:timer *75% 75%
  20752. o:vi timer.c p:vi timer.c *76% 76%
  20753. o:cc -o timer timer.c p:cc -o timer timer.c *76% 76%
  20754. o:timer p:timer *77% 77%
  20755. o:vi timer.c p:vi timer.c *77% 77%
  20756. o:cc -o timer timer.c p:cc -o timer timer.c *78% 78%
  20757. o:timer p:timer *78% 78%
  20758.  
  20759. o:vi timer.c p:vi timer.c *79% 79%
  20760. o:timer p:cc -o timer timer.c 77% 79%
  20761. o:vi timer.c p:vi timer.c *78% 79%
  20762. o:timer p:cc -o timer timer.c 76% 79%
  20763. o:vi timer.c p:vi timer.c *76% 79%
  20764. o:cc -o timer timer.c p:cc -o timer timer.c *77% 79%
  20765. o:timer p:timer *77% 79%
  20766. o:vi timer.c p:vi timer.c *78% 79%
  20767. o:timer p:cc -o timer timer.c 76% 79%
  20768. o:mail p:vi timer.c 75% 79%
  20769. o:remsh rigel p: 74% 79%
  20770. o:vi timer.c p: 72% 79%
  20771. o:cc -o timer timer.c p:cc -o timer timer.c *73% 79%
  20772. o:timer p:timer *73% 79%
  20773. o:vi timer.c p:vi timer.c *74% 79%
  20774. o:cc -o timer timer.c p:cc -o timer timer.c *74% 79%
  20775. o:vi timer.c p:timer 73% 79%
  20776. o:cc -o timer timer.c p:cc -o timer timer.c *73% 79%
  20777. o:more $inc/setjmp.h p:timer 72% 79%
  20778. o:vi timer.c p: 71% 79%
  20779. o:cc -o timer timer.c p:cc -o timer timer.c *72% 79%
  20780. o:vi timer.c p:timer 71% 79%
  20781. o:cc -o timer timer.c p:cc -o timer timer.c *71% 79%
  20782. o:vi timer.c p:timer 70% 79%
  20783. o:cc -o timer timer.c p:cc -o timer timer.c *70% 79%
  20784. o:timer p:timer *71% 79%
  20785. o:vi timer.c p:vi timer.c *71% 79%
  20786. o:cc -o timer timer.c p:cc -o timer timer.c *72% 79%
  20787. o:timer p:timer *72% 79%
  20788. o:vi timer.c p:vi timer.c *72% 79%
  20789. o:cc -o timer timer.c p:cc -o timer timer.c *73% 79%
  20790. o:timer p:timer *73% 79%
  20791. Figure 2 The sample session with an exception removed
  20792. Example 2: (Without ls)
  20793. =======================
  20794. Original Command Stream Predicted Command Stream
  20795. ----------------------- ------------------------ --- ---
  20796. o:vi timer.c p: 00% 00%
  20797. o:cc -o timer timer.c p: 00% 00%
  20798. o:timer p: 00% 00%
  20799. o:vi timer.c p: 00% 00%
  20800. o:cc -o timer timer.c p:cc -o timer timer.c *20% 20%
  20801. o:timer p:timer *33% 33%
  20802. o:vi timer.c p:vi timer.c *42% 42%
  20803. o:vi tstin p:cc -o timer timer.c 37% 42%
  20804. o:cc -o timer timer.c p: 33% 42%
  20805. o:vi tstin p:timer 30% 42%
  20806. o:vi timer.c p:cc -o timer timer.c 27% 42%
  20807. o:cc -o timer timer.c p:cc -o timer timer.c *33% 42%
  20808. o:timer p:timer *38% 42%
  20809. o:vi timer.c p:vi timer.c *42% 42%
  20810. o:cc -o timer timer.c p:cc -o timer timer.c *46% 46%
  20811. o:timer p:timer *50% 50%
  20812. o:vi timer.c p:vi timer.c *52% 52%
  20813. o:cc -o timer timer.c p:cc -o timer timer.c *55% 55%
  20814. o:timer p:timer *57% 57%
  20815. o:vi timer.c p:vi timer.c *60% 60%
  20816. o:cc -o timer timer.c p:cc -o timer timer.c *61% 61%
  20817. o:timer p:timer *63% 63%
  20818.  
  20819. o:vi timer.c p:vi timer.c *65% 65%
  20820. o:cc -o timer timer.c p:cc -o timer timer.c *66% 66%
  20821. o:timer p:timer *68% 68%
  20822. o:vi timer.c p:vi timer.c *69% 69%
  20823. o:cc -o timer timer.c p:cc -o timer timer.c *70% 70%
  20824. o:timer p:timer *71% 71%
  20825. o:vi timer.c p:vi timer.c *72% 72%
  20826. o:cc -o timer timer.c p:cc -o timer timer.c *73% 73%
  20827. o:timer p:timer *74% 74%
  20828. o:vi timer.c p:vi timer.c *75% 75%
  20829. o:cc -o timer timer.c p:cc -o timer timer.c *75% 75%
  20830. o:timer p:timer *76% 76%
  20831. o:vi timer.c p:vi timer.c *77% 77%
  20832. o:cc -o timer timer.c p:cc -o timer timer.c *77% 77%
  20833. o:timer p:timer *78% 78%
  20834. o:vi timer.c p:vi timer.c *78% 78%
  20835. o:cc -o timer timer.c p:cc -o timer timer.c *79% 79%
  20836. o:timer p:timer *80% 80%
  20837. o:vi timer.c p:vi timer.c *80% 80%
  20838. o:cc -o timer timer.c p:cc -o timer timer.c *80% 80%
  20839. o:timer p:timer *81% 81%
  20840. o:vi timer.c p:vi timer.c *81% 81%
  20841. o:cc -o timer timer.c p:cc -o timer timer.c *82% 82%
  20842. o:timer p:timer *82% 82%
  20843. o:vi timer.c p:vi timer.c *82% 82%
  20844. o:timer p:cc -o timer timer.c 81% 82%
  20845. o:vi timer.c p:vi timer.c *81% 82%
  20846. o:timer p:cc -o timer timer.c 80% 82%
  20847. o:vi timer.c p:vi timer.c *80% 82%
  20848. o:cc -o timer timer.c p:cc -o timer timer.c *80% 82%
  20849. o:timer p:timer *81% 82%
  20850. o:vi timer.c p:vi timer.c *81% 82%
  20851. o:timer p:cc -o timer timer.c 80% 82%
  20852. o:mail p:vi timer.c 78% 82%
  20853. o:remsh rigel p: 77% 82%
  20854. o:vi timer.c p: 75% 82%
  20855. o:cc -o timer timer.c p:cc -o timer timer.c *76% 82%
  20856. o:timer p:timer *76% 82%
  20857. o:vi timer.c p:vi timer.c *77% 82%
  20858. o:cc -o timer timer.c p:cc -o timer timer.c *77% 82%
  20859. o:vi timer.c p:timer 76% 82%
  20860. o:cc -o timer timer.c p:cc -o timer timer.c *76% 82%
  20861. o:more $inc/setjmp.h p:timer 75% 82%
  20862. o:vi timer.c p: 74% 82%
  20863. o:cc -o timer timer.c p:cc -o timer timer.c *74% 82%
  20864. o:vi timer.c p:timer 73% 82%
  20865. o:cc -o timer timer.c p:cc -o timer timer.c *73% 82%
  20866. o:vi timer.c p:timer 72% 82%
  20867. o:cc -o timer timer.c p:cc -o timer timer.c *73% 82%
  20868. o:timer p:timer *73% 82%
  20869. o:vi timer.c p:vi timer.c *73% 82%
  20870. o:cc -o timer timer.c p:cc -o timer timer.c *74% 82%
  20871. o:timer p:timer *74% 82%
  20872. o:vi timer.c p:vi timer.c *75% 82%
  20873. o:cc -o timer timer.c p:cc -o timer timer.c *75% 82%
  20874. o:timer p:timer *75% 82%
  20875.  
  20876. Listing 1 Definition of the Predictor
  20877. enum ReturnValue
  20878.  
  20879. {
  20880. Failure,
  20881. Success
  20882. };
  20883.  
  20884. const DEF_CARDINAL=500; //number of commands a_matrix can hold
  20885. const DEF_DELTA_CARDINAL=100; //increment of cardinal_a_matrix
  20886. const MAX_SIZE_COMMAND=50; //maximum size of command
  20887.  
  20888. class predictor
  20889. {
  20890. int num_commands; //current number of unique commands
  20891. int cardinal_a_matrix; //max number of commands a_matrix can hold
  20892. int *a_matrix; //pointer to the adjacency matrix
  20893. char *commands; //list of unique commands
  20894. int last_ordinal; //index of last command
  20895.  
  20896. ReturnValue size(int cardinality); //malloc or remalloc to create
  20897. // approp. a_matrix & commands
  20898. int is_unique(char *command); //checks commands[] for command
  20899. // <0 =>No,else=>index of command
  20900. ReturnValue insert(char *command); //add command to commands
  20901. ReturnValue update(int ordinal); //increment a_matrix entry
  20902.  
  20903. public:
  20904. predictor()
  20905. {
  20906. num_commands=0;
  20907. cardinal_a_matrix=DEF_CARDINAL;
  20908. size(cardinal_a_matrix);
  20909. last_ordinal=-1;
  20910. }
  20911. predictor(int cardinality) //overload constructor
  20912. {
  20913. num_commands=0;
  20914. cardinal_a_matrix=cardinality;
  20915. size(cardinal_a_matrix);
  20916. last_ordinal=-1;
  20917. }
  20918. ~predictor() //destructor
  20919. {
  20920. cardinal_a_matrix=0;
  20921. free(a_matrix);
  20922. free(commands);
  20923. }
  20924.  
  20925. ReturnValue PutNextCommand(char *);
  20926. ReturnValue GetNextCommand(char *);
  20927. int WhenNextCommand(char *);
  20928. };
  20929.  
  20930.  
  20931. ReturnValue predictor::PutNextCommand(char *command)
  20932. {
  20933. int ordinal;
  20934.  
  20935. if(num_commands+1 >= cardinal_a_matrix) //no space ?
  20936. {
  20937. cardinal_a_matrix+=DEF_DELTA_CARDINAL;
  20938.  
  20939. if(size(cardinal_a_matrix)==Failure)//can not allocate space
  20940. return(Failure);
  20941. }
  20942.  
  20943. if((ordinal=is_unique(command))==-l) //command encountered before?
  20944. {
  20945. if(insert(command) == Failure) //no - add it
  20946. return(Failure);
  20947. ordinal=num_commands; //new ordinal number of command
  20948. }
  20949.  
  20950. return(update(ordinal)); //return appropriate value
  20951. }
  20952.  
  20953.  
  20954. ReturnValue predictor::GetNextCommand(char *command)
  20955. {
  20956. int ordinal,max=0,i;
  20957. int *last_command;
  20958.  
  20959. //note: assumes a_matrix is stored in row major fashion
  20960.  
  20961. last_command=a_matrix+ //calculate row of last_command
  20962. last_ordinal*
  20963. cardinal_a_matrix*sizeof(int); //size of a row
  20964. //Note: With certain compilers, sizeof(int) is unnecessary
  20965.  
  20966. for(i=0; i<num_commands; ++i) //do MAX() function
  20967. {
  20968. if(*(last_command+i*sizeof(int))>max)
  20969. {
  20970. max=*(last_command+i*sizeof(int));
  20971. ordinal =i;
  20972. }
  20973. }
  20974.  
  20975. if(max>0)
  20976. {
  20977. strcpy(command,commands*MAX_SIZE_COMMAND);
  20978. return(Success);
  20979. }
  20980. else
  20981. {
  20982. command=NULL;
  20983. return(Failure);
  20984. }
  20985. }
  20986.  
  20987. int predictor::WhenNextCommand(char *command)
  20988. {
  20989. int count=1;
  20990. int old_last_ordinal=last_ordinal; //save state of predictor
  20991. int any_cycle[num_commands]; //check to see if we have cycled
  20992. int i;
  20993. char pcommand[MAX_SIZE_COMMAND];
  20994.  
  20995. //zero
  20996. for(i=0; i<num_commands; ++i)
  20997. any_cycle[i]=0;
  20998.  
  20999.  
  21000. while(GetNextCommand(command)==Success)
  21001. {
  21002. if(!strcmp(pcommand,command)) //found the command
  21003. break;
  21004.  
  21005. i=is_unique(pcommand); //find index of predicted command
  21006. if(!any_cyle[i]^1) //test and check
  21007. {
  21008. count=0; //we have cycled - failure
  21009. break;
  21010. }
  21011.  
  21012. ++count;
  21013. }
  21014. return(count);
  21015. }
  21016.  
  21017. /* End of File */
  21018.  
  21019.  
  21020.  
  21021.  
  21022.  
  21023.  
  21024.  
  21025.  
  21026.  
  21027.  
  21028.  
  21029.  
  21030.  
  21031.  
  21032.  
  21033.  
  21034.  
  21035.  
  21036.  
  21037.  
  21038.  
  21039.  
  21040.  
  21041.  
  21042.  
  21043.  
  21044.  
  21045.  
  21046.  
  21047.  
  21048.  
  21049.  
  21050.  
  21051.  
  21052.  
  21053.  
  21054.  
  21055.  
  21056.  
  21057.  
  21058.  
  21059.  
  21060.  
  21061.  
  21062. C Elements of Style
  21063.  
  21064.  
  21065. Dwayne Phillips
  21066.  
  21067.  
  21068. Dwayne Phillips works as a computer and electronics engineer with the U.S.
  21069. Department of Defense. He has a PhD in Electrical and Computer Engineering
  21070. from Louisiana State University. His interests include computer vision,
  21071. artificial intelligence, software engineering, and programming languages.
  21072.  
  21073.  
  21074. Steve Oualline's C Elements of Style is, as its name suggests, a style manual
  21075. for C programming. Qualline outlines a method for writing clear and
  21076. decipherable C code. He emphasizes a simple and straightforward style. In his
  21077. words, this book is for programmers "who want their programs to be easily read
  21078. and maintained by others."
  21079.  
  21080.  
  21081. Audience
  21082.  
  21083.  
  21084. You do not need to be a C wizard to understand this book. In fact, wizards who
  21085. write statements like:
  21086. *destination++ = *source++
  21087. may not like this book. Oualline advocates the more accessible:
  21088. *destination = *source; destination++; source++;
  21089. If you write programs that must be corrected or augmented by others, this book
  21090. will be of interest to you. It is especially appropriate for relatively new
  21091. programmers who are struggling to learn good C programming habits.
  21092.  
  21093.  
  21094. Contents
  21095.  
  21096.  
  21097. C Elements of Style has nine chapters, a compact style manual, two appendices,
  21098. and an index. The chapters cover the topics expected in this type of book.
  21099. They include (1) style and program organization, (2) file basics, comments,
  21100. and program headings, (3) variable names, (4) statement formatting, (5)
  21101. statement details, (6) the preprocessor, (7) C++ style, (8) directory
  21102. organization and makefile style, and (9) user-friendly programming.
  21103. For much of the book, Oualline gives examples of good and bad code, and
  21104. summarizes with style rules. His rules are simple and easy to apply. Some of
  21105. the rules apply to the process of writing code. For example, "Comment your
  21106. code as you write it." (This rule, he says, saves time in the long run.) Other
  21107. rules address the code itself: "Constant names are all upper case" and "Follow
  21108. every variable declaration with a comment that defines it." Some rules just
  21109. make a programmer's life easier: "Assume that *,/, and % come before + and -.
  21110. Put parentheses around everything else."
  21111. The style rules are aimed at making programs readable and reliable. Good
  21112. variable and subroutine names, which help make the code explain itself, and
  21113. good comments, which help reveal the organization of programs, enhance
  21114. readability. Oualline strives to increase reliability by limiting the code to
  21115. safe subsets of the C language. (Not allowing shortcuts such as *destination++
  21116. = *source++ reduces the risk of problematic side effects.) Oualline describes
  21117. this emphasis on readable and reliable code as "defensive programming" or
  21118. "doing a lot of thinking so I don't have to do a lot of thinking."
  21119. The chapter on makefile style is outstanding and to my knowledge unique.
  21120. Makefiles are essential to C programming and are often the hardest part of a
  21121. project to understand. Other books describe how to use makefiles, but this is
  21122. the only book I have seen that tells how to organize and format them. Oualline
  21123. formally defines many makefile items that have become pseudo standards over
  21124. the years. This material may seem trivial to an experienced UNIX C programmer,
  21125. but many C programmers today have never worked on a UNIX system (believe it or
  21126. not).
  21127. The chapter on user-friendly programming is a bit out of place, but useful. It
  21128. discusses how to write programs that users will actually use. (It includes the
  21129. Law of Least Astonishment: The program should act in a way that least
  21130. astonishes the user.)
  21131. The style manual is 35 pages of rules without all the discussion. For those
  21132. short on time, this book-within-a-book provides a concise summary of the text.
  21133. The first appendix shows three complete code examples (two in C, one in C++)
  21134. that employ the style rules given in the body of the book. These examples show
  21135. Oualline's recommendations in practice. The second appendix lists all the
  21136. rules given in the chapters. (Take these seven pages and pin them on the wall
  21137. next to the coffee machine.)
  21138.  
  21139.  
  21140. Other Style Manuals
  21141.  
  21142.  
  21143. For years, the only programming style manual available was Kernighan and
  21144. Plauger's thin little classic The Elements of Programming Style [1].
  21145. Fortunately, several style manuals have appeared in the recent past -- each
  21146. written with a different point of view.
  21147. Plum's C Programming Guidelines [2] and C++ Programming Guidelines [3]
  21148. (reviewed in The C Users Journal, January 1993) are comprehensive references
  21149. on programming standards and style. They are written in a compact,
  21150. subroutine-like style that is appropriate for reference books. These books are
  21151. technical and not for the novice.
  21152. Steve McConnel's Code Complete [4] is a comprehensive handbook for
  21153. programmers. As such, it includes plenty of good advice on programming style,
  21154. but its style sections are scattered throughout the 800-plus pages.
  21155. While Oualline favors code that anyone can understand, Ranade and Nash's The
  21156. Elements of C Programming Style [5] (reviewed in The C Users Journal, July
  21157. 1993) encourages programmers to master and exploit the notation of the C
  21158. language. (Ranade and Nash's do recommend backing away from terse C style if
  21159. the audience includes programmers who have a limited knowledge of C.)
  21160.  
  21161.  
  21162. Conclusion
  21163.  
  21164.  
  21165. Oualline's book is the closest to Kernighan and Plauger's and it serves as a
  21166. worthy update to that earlier classic. It is a short, yet complete treatment
  21167. of C style.
  21168. C Elements of Style is a deserving addition to the desk top -- not just the
  21169. bookshelf.
  21170. References
  21171. [1] The Elements of Programming Style, second edition, Brian W. Kernighan,
  21172. P.J. Plauger, McGraw Hill, New York, New York, 1978, ISBN 0-07-034207-5.
  21173. [2] C Programming Guidelines, second edition, Thomas Plum, Plum Hall Inc.,
  21174. ISBN 0-911537-07-4.
  21175. [3] C++ Programming Guidelines, Thomas Plum and Dan Saks, Plum Hall Inc., ISBN
  21176. 0-911537-10-4.
  21177. [4] Code Complete, A Practical Handbook of Software Construction, Steve
  21178. McConnel, Microsoft Press, One Microsoft Way, Redmond, Wash. 98052-6399, ISBN
  21179. 1-55615-484-4.
  21180. [5] The Elements of C Programming Style, Jay Ranade and Alan Nash, McGraw
  21181. Hill, New York, New York, 1993, ISBN 007-051278-7.
  21182. Title: C Elements of Style The Programmer's Style Manual for Elegant C and C++
  21183. Programs
  21184. Author: Steve Oualline
  21185. Publisher: M&T Books
  21186.  
  21187.  411 Borel Ave, Suite 100
  21188.  San Mateo, CA 94402
  21189.  1992
  21190. Price: $21.95
  21191. Pages: 265
  21192. ISBN: 1-55851-291-8
  21193.  
  21194.  
  21195.  
  21196.  
  21197.  
  21198.  
  21199.  
  21200.  
  21201.  
  21202.  
  21203.  
  21204.  
  21205.  
  21206.  
  21207.  
  21208.  
  21209.  
  21210.  
  21211.  
  21212.  
  21213.  
  21214.  
  21215.  
  21216.  
  21217.  
  21218.  
  21219.  
  21220.  
  21221.  
  21222.  
  21223.  
  21224.  
  21225.  
  21226.  
  21227.  
  21228.  
  21229.  
  21230.  
  21231.  
  21232.  
  21233.  
  21234.  
  21235.  
  21236.  
  21237.  
  21238.  
  21239.  
  21240.  
  21241.  
  21242.  
  21243.  
  21244.  
  21245.  
  21246.  
  21247.  
  21248.  
  21249.  
  21250. Standard C
  21251.  
  21252.  
  21253. C++ Language Support Library
  21254.  
  21255.  
  21256.  
  21257.  
  21258. P.J. Plauger
  21259.  
  21260.  
  21261. P.J. Plauger is senior editor of The C Users Journal. He is convenor of the
  21262. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  21263. latest books are The Standard C Library, and Programming on Purpose (three
  21264. volumes), all published by Prentice-Hall. You can reach him at
  21265. pjp@plauger.com.
  21266.  
  21267.  
  21268.  
  21269.  
  21270. Introduction
  21271.  
  21272.  
  21273. I conclude my discussion of the language support portion of the library
  21274. specified by the draft C++ standard. (See "Standard C: The Header
  21275. <exception>," CUJ, February 1994, "The C Library in C++," CUJ, December 1993,
  21276. "C++ Library Ground Rules," CUJ, November 1993, and "Developing the Standard
  21277. C++ Library," CUJ, October 1993.)
  21278. "Language support" consists of those functions that can be called implicitly
  21279. by C++ code, even when the code apparently contains no function calls. It also
  21280. consists of the types required to declare and use those functions, as well as
  21281. a few other related functions and types not directly needed to support the C++
  21282. language proper.
  21283. Standard C has few such creatures (if any). You can argue that several type
  21284. definitions are part of language support. The types ptrdiff_t, size_t, and
  21285. wchar_t are defined in various headers so you can declare objects that have
  21286. the same types as certain expressions. They are a way to convey otherwise
  21287. unknowable (or hard to learn) information about these types from the
  21288. translator to the program.
  21289. You can also argue that function exit is part of language support. The
  21290. execution of any C program effectively occurs by evaluating the expression
  21291. exit(main(argc, argv)). Saying such a thing simplifies descriptions -- it ties
  21292. together the effects of calling exit and returning from main, for example. But
  21293. it doesn't have a dramatic effect on how you actually write C programs. You
  21294. cannot, for example, provide your own version of exit and expect it to be
  21295. called when main returns. (And many implementations don't really call exit
  21296. when main returns, but some other underlying function instead.)
  21297. C++, on the other hand, offers numerous opportunities along these lines. You
  21298. can trot up all sorts of functions that get control, either directly or
  21299. indirectly, when one of the language support functions gets called. For
  21300. example, in last month's discussion of exceptions I identified three functions
  21301. that let you register handlers, or functions that get control under some
  21302. circumstances:
  21303. set_terminate, to specify a handler for calls to terminate() set_unexpected,
  21304. to specify a handler for calls to unexpected()
  21305. xmsg::set_raise_handler, to specify a handler for calls to xmsg::raise()
  21306. You can also derive a class from xmsg and override the virtual xmsg::do_raise
  21307. to get control when certain exceptions get reported by executing code.
  21308. Thus, the draft C++ standard is a bit harder to write than the C Standard. In
  21309. C, the implementation provides all library functions and you the programmer
  21310. cannot displace them. The C Standard only has to describe a single interface
  21311. between implementation and program. In C++, however, the program can displace
  21312. functions otherwise supplied by the library. The draft C++ standard must spell
  21313. out the environment promised to such a displacing function. And it must spell
  21314. out what is expected of the displacing function so the program doesn't get
  21315. surprised.
  21316. A handler for terminate(), for example, is not supposed to return to its
  21317. caller. If you provide one that prints a message and returns, you can cause
  21318. the library severe problems. The draft C++ standard says so. So when you read
  21319. the descriptions that follow, remember that the "treaty" between programmer
  21320. and implementor can be multifaceted. The extra complexity of the draft C++
  21321. standard is one of the prices we pay for extra flexibility in this area.
  21322.  
  21323.  
  21324. Storage Allocation
  21325.  
  21326.  
  21327. Exceptions can be thought of as a way to structure the use of setjmp and
  21328. longjmp. Similarly, the addition of new and delete to C++ essentially
  21329. structure the use of malloc and free. By writing:
  21330. Thing *p = new Thing;
  21331. you are assured that the object of type Thing is properly constructed after it
  21332. is successfully allocated and before it can be accessed through p. Similarly,
  21333. the expression statement:
  21334. delete p;
  21335. ensures that the object is destroyed before its storage is deallocated.
  21336. You don't have to include any headers before writing expressions like these --
  21337. new and delete are indeed built right into the language. But you can also play
  21338. a variety of games with storage allocation if you choose. To do so, you begin
  21339. by including the header <new>. Listing 1 shows a representative version of
  21340. this header. I omit the extra superstructure required by namespaces, because
  21341. it is distracting and still in a state of flux.
  21342. The simplest game you can play is to gain control when space for the heap is
  21343. exhausted. The function set_new_handler lets you register a handler for this
  21344. condition. In principle, the draft C++ standard says you can "make more
  21345. storage available for allocation and then return," but it fails to describe a
  21346. portable way to do so. Calling free to liberate storage may help, but there is
  21347. no requirement that storage be actually allocated by calling malloc. Deleting
  21348. one or more allocated objects may also help, but even that is not guaranteed.
  21349. More likely, you will want to throw an exception or terminate execution at
  21350. this point.
  21351.  
  21352.  
  21353. xalloc Exceptions
  21354.  
  21355.  
  21356. The default "new handler" does, in fact, throw an exception now. As I
  21357. described last month, all library exceptions are derived from the base class
  21358. xmsg. Moreover, all exceptions are thrown by calling ex.raise(), for some
  21359. object ex of class xmsg. Unless you seize control of the process in one of the
  21360. ways I described last month, the eventual outcome is that a failed allocation
  21361. will throw an exception, which will in turn terminate execution of the
  21362. program.
  21363. This is a significant change from universal past practice, which has been to
  21364. quietly yield a null pointer as a result of the new expression. The Library
  21365. Working Group of X3J16/WG21, the joint ANSI/ISO standards committee for C++,
  21366. anguished quite a bit before recommending this change. The joint committee
  21367. anguished a bit more in turn. But eventually, the predominant wisdom was that
  21368. the Standard C++ library had bloody well better use the full language in this
  21369. case, not just the bits that were available when new and delete were first
  21370. added to C++.
  21371. A persuasive argument is that very few programs truly check all new
  21372. expressions for null pointers. Those that don't may well stumble about when
  21373. the heap is exhausted -- they're almost certainly better off dying a clean
  21374. death. Those that do check all such expressions often simply abort -- the path
  21375. to abnormal termination is now just slightly different. It is only those few
  21376. sophisticated programs that try to do something nontrivial when heap is
  21377. exhausted that need a bit of rewriting. Most of the joint committee felt this
  21378. was a necessary price to pay to introduce exceptions at this critical
  21379. juncture.
  21380. Even so, some sympathy remains for being able to revert to the old behavior.
  21381. For a variety of reasons, the Library Working Group has not spelled out a
  21382. portable way to do so. But the group has identified what it thinks should be a
  21383. common extension. Calling set_new_handler with a null pointer argument is
  21384. otherwise undefined behavior. It seems natural to use this nonportable call as
  21385. a way for implementations to know that they should revert to the older
  21386. behavior.
  21387.  
  21388.  
  21389. Replacing operator new(size_t)
  21390.  
  21391.  
  21392. If you want more certain control over the business of allocating storage, your
  21393. best bet is to provide your own versions of operator new(size_t) and/or
  21394. operator delete(void *). These functions have a peculiar dispensation -- the
  21395. library provides a version of each, but you can "knock out" those versions by
  21396. defining your own. (Only the array versions of these two operators, described
  21397. below, also enjoy this special status within the Standard C++ library.)
  21398. Before I go into details, please note an important distinction here. When you
  21399. write:
  21400.  
  21401. Thing *p = new Thing;
  21402. the new Thing part is called a "new expression." It calls operator new(size_t)
  21403. to allocate storage, but it also does other things, such as constructing the
  21404. newly allocated object. All that operator new(size_t) has to worry about is
  21405. providing the number of requested bytes, suitably aligned, or dealing with
  21406. heap exhaustion. Listing 3 shows one way to write this function.
  21407. Similarly, when you write:
  21408. delete p;
  21409. the delete p part is called a "delete expression." It calls operator
  21410. delete(void *) to free storage, but it first destroys the object (only if the
  21411. pointer is not null, of course). All that operator delete(void *) has to worry
  21412. about is freeing storage for the object. Listing 4 shows one way to write this
  21413. function.
  21414. So one thing you might do is replace operator delete(void *) with a function
  21415. that doesn't really free the storage. That could be handy while you're
  21416. debugging a program, provided of course that you have enough heap to run your
  21417. test cases.
  21418. Or you might replace both operator new(size_t) and operator delete(void *)
  21419. with versions that are simpler, or faster, or more sophisticated than the
  21420. library versions. It is important to replace both, because the latter function
  21421. in the library only knows how to free storage for objects allocated by the
  21422. former.
  21423. In either case, you probably don't have to bother with set_new_handler. You
  21424. are at liberty to do whatever you want when you run out of heap. No need to
  21425. call the new handler, which you can't easily do portably anyway.
  21426.  
  21427.  
  21428. Placement Syntax
  21429.  
  21430.  
  21431. Yet another latitude granted by the C++ language is to provide an arbitrary
  21432. set of additional arguments in a new expression, as in:
  21433. Thing *get_special(T1 stuff,
  21434. T2 more_stuff)
  21435. {
  21436. return (new (stuff, more_stuff) Thing);
  21437. }
  21438. This form implicitly calls the function:
  21439. void *operator new(size_t, T1, T2);
  21440. which you are obliged to supply. I leave it to your imagination what extra
  21441. parameters might be useful when you're allocating some of your more
  21442. sophisticated objects.
  21443. It doesn't take too much imagination, however, to see a very common need.
  21444. Sometimes you know exactly where you want a C++ object to be constructed --
  21445. you have reason to believe that the storage area X is large enough and
  21446. suitably aligned to hold an object of type Thing. Moreover, you're confident
  21447. that no object has been constructed there already for which a destructor will
  21448. later be called. (Whew!)
  21449. To deal with this twilight zone between C and C++ programming, you can write:
  21450. Thing *p = new ((void *)&X) Thing;
  21451. This, naturally enough, calls the function:
  21452. void *operator new(size_t, void *);
  21453. which can simply return its second argument, as shown in Listing 4. The
  21454. Standard C library provides this one version of a placement operator new.
  21455. (Don't forget to include the header <new> to be sure it is properly declared.)
  21456. Any fancier placement variants are up to you to provide.
  21457.  
  21458.  
  21459. Member operator new
  21460.  
  21461.  
  21462. Yet another way exists for controlling how objects get allocated. For any
  21463. class, you can overload all the variants of operator new and/or operator
  21464. delete that I've mentioned so far. Perhaps you want to write your own versions
  21465. of:
  21466. void *Thing::operator new(size_t);
  21467. void Thing::operator delete(void *);
  21468. that does a really fast job of allocating and freeing objects of class Thing.
  21469. It can, for example, maintain a list of previously freed objects and hand them
  21470. back quickly for future allocation requests. Unless you really get tricky, you
  21471. can even ignore the size_t first argument to all variants of operator new,
  21472. since you know how big a Thing is likely to be. (How do you get tricky? Well,
  21473. you can make operator new virtual in the base class and fail to override it in
  21474. a derived class. But thinking about things like that gives me a headache.)
  21475. So you see that you can exercise pretty fine control over how all objects, or
  21476. even individual objects, get allocated.
  21477.  
  21478.  
  21479. Allocating Arrays
  21480.  
  21481.  
  21482. But that leads to one last residual problem, regarding the allocation and
  21483. freeing of arrays. You can, for example, write:
  21484. Thing *p = new Thing[N];
  21485. to allocate an array of N elements each of type Thing. Each of the elements is
  21486. constructed in order, starting with the first (element zero). In this case,
  21487. you must write the expression statement:
  21488. delete[] p;
  21489. to delete the array, not just a simple:
  21490. delete p;
  21491. as before. Why? Because the "array new expression" above has to somehow
  21492. memorize how many elements N it has allocated. It needs to know to locate this
  21493. memorized information and use it to destroy the appropriate number of elements
  21494. and free the appropriate amount of storage. Yes, some existing implementations
  21495. of C++ let you be cavalier about deleting arrays the wrong way, but don't
  21496. count on that license in a portable program.
  21497. This requirement presents another problem. What happens if you've provided a
  21498. member operator new(size_t) for class Thing, as above? It cannot, in general,
  21499. know whether it's being asked to allocate storage for a single element or a
  21500. whole array. (Remember the potential trickery I mentioned above.) So what C++
  21501. has done in the past is to ignore any such member functions and call the
  21502. global operator new(size_t) for all array allocations. This has been a less
  21503. than satisfactory solution.
  21504. The joint committee has plugged this control gap by permitting you to define
  21505. functions such as the members operator[] new(size_t) and operator delete(void
  21506. *). Defining these functions gives you control over the allocation and freeing
  21507. of arrays of class objects as well as the class objects themselves. You can't
  21508. necessarily tell how many array elements are being allocated, by the way. An
  21509. array new expression can ask for extra storage for its own bookkeeping, so
  21510. you'd better honor the size_t argument blindly. But at least you can maintain
  21511. private storage pools now for array objects.
  21512. For completeness, the draft C++ standard also includes global versions of:
  21513. void *operator new(size_t);
  21514. void operator delete(void *);
  21515. The library versions of these functions just turn around and call the
  21516. non-array library versions, so I won't show you the code for them. And you can
  21517. indeed knock these functions out with your own definitions, but I'm not sure
  21518. why you'd bother. Doubtless, someone more clever or perverse than I can make a
  21519. case for any feature added to C++.
  21520.  
  21521.  
  21522. Type Information
  21523.  
  21524.  
  21525.  
  21526. There is one last aspect to the language support library. It is rather small
  21527. compared to exceptions (all of last month's installment) or storage management
  21528. (most of this month's). I tack it on here for completeness.
  21529. Another relatively recent significant addition to the draft C++ standard is
  21530. "run-time type identification" (or RTTI, for short). Basically, it adds the
  21531. operator typeid for obtaining various bits of information on the type of an
  21532. object (or expression). The operator yields an object of class typeinfo,
  21533. defined in the header <typeinfo>. Listing 5 shows one way to write this
  21534. header.
  21535. The exception badtypeid is reported in those cases where the type cannot be
  21536. determined statically at translation time. If, in the process of chasing down
  21537. the actual object, the program encounters a null pointer, you can guess what
  21538. happens.
  21539. (If you're put off by all these names made from words run together, you're not
  21540. alone. There's a good chance that the joint committee will approve a new
  21541. naming convention that involves a more liberal use of underscores to separate
  21542. component words in names. So don't be surprised if many of these compound
  21543. names change in the coming months.)
  21544. What can you do with an object of class typeinfo? Well, you can obtain some
  21545. sort of name for the type, for one thing. typeinfo::name() yields a
  21546. null-terminated multibyte string (or NTMBS, in the jargon of the draft C++
  21547. standard) that presumably says something meaningful about the type. There are
  21548. no standard names defined, so far, not even for the builtin types.
  21549. You can also compare two objects of class typeinfo for equality or inequality.
  21550. Within any given program, you can expect two such objects to compare equal
  21551. only if they derive from two expressions of the same type. Don't expect to be
  21552. able to remember these critters in files, however, and check for type equality
  21553. across programs. Even running the same program twice doesn't promise to yield
  21554. the same representation of a typeinfo object for the same type each time. (I
  21555. have indicated that the type information can be represented as an int, but
  21556. that is just illustrative, not a requirement.)
  21557. Finally, you can impose an ordering on all the types within a program.
  21558. typeinfo::before(const typeinfo&) returns nonzero for an object that
  21559. represents a type earlier in the pecking order than the argument object. Once
  21560. again, however, no promises are made about the rules for determining this
  21561. order, or whether they're even the same each time you run the program.
  21562. I'm sure far more can be said about the uses of RTTI, but I'm not the one to
  21563. say it at this point in my career. Even if I were, this is not the place to
  21564. say it. For now, you know what the standard C++ library has to know about
  21565. RTTI.
  21566.  
  21567. Listing 1 The header <new>
  21568. #ifndef _NEW_____LINEEND____
  21569. #define _NEW_____LINEEND____
  21570. #include <exception>
  21571. // class xalloc
  21572. class xalloc : public xruntime {
  21573. protected:
  21574. // virtual void do_raise();
  21575. public:
  21576. xalloc(const char * = 0, const char * = 0);
  21577. virtual ~xalloc();
  21578. };
  21579. // function and object declarations
  21580. fvoid_t *set_new_handler(fvoid_t *);
  21581. void operator delete(void *);
  21582. void operator delete[](void *);
  21583. void *operator new(size_t);
  21584. void *operator new[](size_t);
  21585. void *operator new(size_t, void *);
  21586. extern fvoid_t (*_New_hand);
  21587. #endif
  21588.  
  21589.  
  21590. Listing 2 The function operator new(size_t)
  21591. // operator new(size_t) REPLACEABLE function
  21592. #include <stdlib.h>
  21593. #include <new>
  21594.  
  21595. void *operator new(size_t size)
  21596. { // try to allocate size bytes
  21597. void *p;
  21598. while ((p = malloc(size)) == 0 && _New_hand != 0)
  21599. (*_New_hand)();
  21600. return (p);
  21601. }
  21602.  
  21603. // End of File
  21604.  
  21605.  
  21606. Listing 3 The function operator delete(void *)
  21607. // operator delete(void *) REPLACEABLE function
  21608. #include <stdlib.h>
  21609. #include <new>
  21610.  
  21611. void operator delete(void *p)
  21612. { // free an allocated object
  21613. free(p);
  21614. }
  21615.  
  21616.  
  21617. // End of File
  21618.  
  21619.  
  21620. Listing 4 The function operator new(size_t, void *)
  21621. // operator new(size_t, void *)
  21622. #include <new>
  21623.  
  21624. void *operator new(size_t, void *p)
  21625. { // allocate in place
  21626. return (p);
  21627. }
  21628.  
  21629. // End of File
  21630.  
  21631.  
  21632. Listing 5 The header <typeinfo>
  21633. #ifndef _TYPEINFO_____LINEEND____
  21634. #define _TYPEINFO_____LINEEND____
  21635. // class badtypeid
  21636. class badtypeid : public xlogic {
  21637. protected:
  21638. // virtual void do_raise();
  21639. public:
  21640. badtypeid();
  21641. virtual ~badtypeid();
  21642. };
  21643. // class typeinfo
  21644. class typeinfo {
  21645. const char *_Name;
  21646. const int _Desc; // implementation dependent
  21647. typeinfo(const typeinfo&);
  21648. typeinfo& operator=(const typeinfo&);
  21649. public:
  21650. virtual ~typeinfo();
  21651. int operator==(const typeinfo&) const;
  21652. int operator!=(const typeinfo& _Rop) const
  21653. {return (!(*this == _Rop)); }
  21654. int before(const typeinfo&);
  21655. const char *name() const {return (_Name); }
  21656. };
  21657. #endif
  21658.  
  21659. // End of File
  21660.  
  21661.  
  21662.  
  21663.  
  21664.  
  21665.  
  21666.  
  21667.  
  21668.  
  21669.  
  21670.  
  21671.  
  21672.  
  21673.  
  21674.  
  21675.  
  21676.  
  21677.  
  21678.  
  21679. Questions & Answers
  21680.  
  21681.  
  21682. Run-Time Type Checking in C++
  21683.  
  21684.  
  21685.  
  21686.  
  21687. Kenneth Pugh
  21688.  
  21689.  
  21690. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++
  21691. language courses for corporations. He is the author of All On C, C for COBOL
  21692. Programmers, and UNIX for MS-DOS Users, and was a member of the ANSI C
  21693. committee. He also does custom C/C++ programming and provides
  21694. SystemArchitectonics services. His address is 4201 University Dr., Suite 102,
  21695. Durham, NC 27707. You may fax questions for Ken to (919) 489-5239. Ken also
  21696. receives email at kpugh@allen.com (Internet) and on Compuserve 70125,1142.
  21697.  
  21698.  
  21699.  
  21700.  
  21701. Run-Time Typing
  21702.  
  21703.  
  21704. I have taught myself C++ and am comfortable with most of its concepts. There
  21705. is one aspect that I am unsure about. Bjarne Stroustrup, in his book The C++
  21706. Programming Language states: "using run-time type inquiries ... destroys all
  21707. modularity in a program and negates the aims of object-oriented programming."
  21708. I attempt to adhere to this suggestion, but there is a situation in which I
  21709. don't know how to apply the rule: reading and writing objects to data files.
  21710. As an example, let's suppose we are writing a simple checkbook balancing
  21711. program. The base object is an Entry, which corresponds to something you would
  21712. enter in your checkbook. Derived from base class Entry are three classes which
  21713. can be instantiated: Check (a written bank draft), Deposit (a bank window
  21714. deposit), and Withdrawal (a window withdrawal). Keeping track of the type of
  21715. objects in memory is easy, since when one of the three derived classes is
  21716. created, it is done through a constructor which builds in type information.
  21717. There are many ways to write the checkbook entries to a data file, so let's
  21718. assume we just write a character-based representation to the file. The
  21719. question is how can we write the data so that we can read the information back
  21720. into memory? The only way I can think of is to include type information, such
  21721. as an integer coded for each class type. This method violates Mr. Stroustrup's
  21722. rule, though. Any thoughts you have on this matter would be greatly
  21723. appreciated.
  21724. Paul Waldo
  21725. Forest, VA
  21726. A
  21727. One reason Bjorne was against run-time checking was because it enables
  21728. programmers to avoid derivation. For example, suppose you have a type_of
  21729. function that can identify the Entry type of a pointer. To post an entry, you
  21730. might code something that looks like Listing 1. If you need to add an
  21731. additional type of Entry, then you have to add another case to the switch
  21732. statement.
  21733. However, there's a cleaner way to handle this problem. Suppose you give Entry
  21734. a pure virtual post function. Each class derived from Entry now supplies its
  21735. own post function. The post_entry function could look like Listing 2. When
  21736. adding a new derived class, you do not need to change the post_entry function.
  21737. You just provide a post function for the new class.
  21738. As you have suggested, virtual functions only work while objects are in
  21739. memory. Each object of a class with virtual functions contains a pointer to a
  21740. table of function pointers (the vtable, as it is sometimes referred to). All
  21741. objects of a particular class contain a pointer to the same table. When the
  21742. program calls a virtual member function it uses the pointer to the
  21743. corresponding function in the vtable. In a sense, this vtable pointer uniquely
  21744. identifies the class type of an object. In fact, some compilers have
  21745. non-standard extensions that can use this pointer to provide a form of
  21746. run-time type identification. Other vendors provide alternative methods for
  21747. run-time identification. Microsoft has a CRuntimeClass object associated with
  21748. each class that is used with the IsKindOf function. With this function, you
  21749. can determine if an object belongs to a particular class or if it is derived
  21750. from a class. (The class must be derived from the Microsoft CObject class to
  21751. work with IsKindOf.) To use the IsKindOf function, you include a
  21752. DECLARE_DYNAMIC macro in the class definition and an IMPLEMENT_DYNAMIC macro
  21753. in the class implementation. CRuntimeClass is the data type used to store
  21754. class information. You can obtain a pointer to an object of this type with:
  21755. CRuntimeClass * pclass = RUNTIME_CLASS(Your_class);
  21756. Typically you do not use the information in CRuntimeClass directly, but
  21757. instead pass it to IskindOf. For example, as shown in the following code
  21758. fragment, you might want to cast a base class pointer to a pointer to a real
  21759. object. To be logically correct, you need to be sure the object pointed to
  21760. belongs to a particular class.
  21761. CObject * pyour_object = new Your_class;
  21762. ...
  21763. if ( pyour_object->IsKindOf( RUNTIME_CLASS(Your_class) ) )
  21764. {
  21765. // Cast it
  21766. Your_class * p_this_object =
  21767. (Your_class *) pyour_object;
  21768. }
  21769. You can use this type information to create objects of a given class, The
  21770. CRuntimeClass class provides a member function CreateObject for dynamically
  21771. creating objects. The use of CreateObject is demonstrated as follows:
  21772. CRuntimeClass * p_your_class = RUNTIME CLASS(Your_class);
  21773. // Create it
  21774. CObject * pyour_object =
  21775. p_your_class->CreateObject();
  21776. // Cast it to the class
  21777. Your_class * p_this_object =
  21778. (Your_class *) pyour_object;
  21779. In your question, you have run across one area of programming that requires
  21780. some form of type identification: permanent or persistent storage. You cannot
  21781. use the address of a vtable as the identification means for an object. When
  21782. the contents of an object are read back into memory, the chances are that the
  21783. vtable will be in a different memory location. You will need to store some
  21784. form of type identification with the object. This identification could either
  21785. be an integer value or the string name of the class. If your program stores
  21786. objects in a known sequence and retrieves them by the same known sequence,
  21787. then you do not need any type identification stored with the object. For
  21788. example, if you always store Checks starting at the first position in the
  21789. file, followed by Deposits and then Withdrawals, you can determine the type of
  21790. an object by its position in the file. In your example, you cannot assume this
  21791. kind of ordering exists, so you must store a class identifier. There are lots
  21792. of ways to store this identifier, depending on how your classes are organized.
  21793. Let's assume you have some unique identifier for each account entry type, such
  21794. as:
  21795. enum Entry_type {Entry_check, Entry_deposit,
  21796. Entry_withdrawal};
  21797. One of these values would be stored away with each object. For example, if
  21798. your compiler provides run-time type identification with type_of, you might
  21799. code:
  21800. Entry::save()
  21801. {
  21802. if ( this->type_of() == Check )
  21803. // Store Entry_check
  21804. else if (this->type_of() ==
  21805. Deposit )
  21806. // Store Entry_deposit
  21807. else if (this->type_of() ==
  21808. Withdrawal )
  21809.  
  21810. // Store Entry_withdrawal)
  21811. ...
  21812. // Store remaining contents
  21813. // in an account
  21814. }
  21815. Before I get too much mail regarding this "hidden switch statement," let me
  21816. explain that this function is complementary to the retrieval function, which I
  21817. will show shortly. You could use a virtual function for save in each of the
  21818. derived classes. The function would store the appropriate Entry_type value,
  21819. call a save function in the Entry class to store the data members in that
  21820. class, and then save its own data members. If you were using the Microsoft
  21821. compiler, this type checking code might look like the following fragment.
  21822. However, Microsoft provides other functions that make this code unnecessary,
  21823. as we shall see shortly.
  21824. Entry::save()
  21825. {
  21826. CRuntimeClass * pcheck_class
  21827. = RUNTIME_CLASS(Check);
  21828. CRuntimeClass * pdeposit_class
  21829. = RUNTIME_CLASS(Deposit);
  21830. CRuntimeClass * pwithdrawal_class
  21831. = RUNTIME_CLASS(Withdrawal );
  21832. if ( this->IsKindOf(pcheck) )
  21833. // Store Entry_check
  21834. else if (this->IsKindOf(pdeposit) )
  21835. // Store Entry_deposit
  21836. else if (this->IsKindOf(pwithdrawal) )
  21837. // Store Entry_withdrawal)
  21838. ...
  21839. // Store remaining contents in an account
  21840. }
  21841. The basic dilemma comes in retrieving the objects. You cannot use a retrieve
  21842. function for each derived class, as you do not know the type of entry for the
  21843. next object stored in the file. Thus you can only retrieve an entry as a base
  21844. class object. For example, the caller might use something that looks like:
  21845. Entry *pentry;
  21846. Account account;
  21847. ...
  21848. account.retrieve_next(&pentry);
  21849. The retrieve_next function could look like the following:
  21850. int Account::retrieve_next(Entry **pentry_in)
  21851. {
  21852. // Get rid of old pointer
  21853. Entry *pentry = *pentry_in;
  21854. if (*pentry != NULL)
  21855. delete pentry;
  21856. // Read the record off disk
  21857. // Then check the type of the record ready
  21858. if (type == Entry_check)
  21859. *pentry = new Check;
  21860. else if (type == Entry_deposit)
  21861. *pentry = new Deposit;
  21862. else if (type == Entry_withdrawal)
  21863. *pentry = new Withdrawal;
  21864. // Move associated data into the type
  21865. ...
  21866. // Then return the pointer
  21867. *pentry_in = pentry;
  21868. }
  21869. The caller passes this function a pointer to pointer to the Entry class. The
  21870. function deletes the Entry that was pointed to by pentry_in. The Entry class
  21871. should provide a virtual destructor, so that any additional data members in
  21872. the derived classes are deallocated. Based on the Entry_type value stored in
  21873. the file, the function allocates the appropriate pointer. The function moves
  21874. the necessary data from the file into the new object and returns the value of
  21875. the pointer. If necessary, each derived class can include a function that
  21876. reads any additional data members from the file.
  21877. The Microsoft archive retrieval function (CArchive::operator>>) does not
  21878. require this if-else structure. The storage function (CArchive::operator<<)
  21879. stores a run-time class identifier (the name of the class) in the file. When
  21880. the retrieval function reads the file, it dynamically creates an object of the
  21881. stored class and loads the information from the file into that object.
  21882. Microsoft's multi-pronged approach to run-time typing eliminates worries about
  21883. the details of persistent object storage and retrieval. All you have to do is
  21884. to include the necessary macros in your object header file and your object
  21885. implementation file.
  21886.  
  21887.  
  21888. C++ Problems
  21889.  
  21890.  
  21891. I am an agricultural economist at the U.S. Department of Agriculture and am
  21892. trying to learn C and C++. I have bought over ten books on C, and am reading
  21893. them and keying in the exercises to practice the concepts. One of the books I
  21894. have bought is Learning C++, by Neil Graham. I began keying in one of the
  21895. exercises (in C++) and the program gave me a bunch of iostream declaration
  21896. errors when I attempted to compile the source code using Borland Turbo C++,
  21897. 1991 (Listing 3). I saw your column in The C Users Journal and thought that
  21898. you might be willing to suggest what is wrong, and how I might fix it. Also,
  21899. do you have any recommendations for textbooks and/or diskette tutorials that
  21900. may be useful in learning C and C++?
  21901. Kenneth W. Erickson
  21902. Washington, D.C.
  21903. A
  21904.  
  21905. Reading several books and trying their examples is an excellent way to learn a
  21906. new language. You can get a good feel for alternative ways of approaching
  21907. problems. However, when you use only books for learning, you often run into
  21908. problems for which a book has no answer. Many times I've been in the same
  21909. quandary when using vendor-supplied manuals. In some cases the solution to my
  21910. problem was simple but the manual just didn't deal with the problem.
  21911. In your case, what appears to be an unexplainable error is caused by a simple
  21912. glitch. You named the program with a ".c" extension. The compiler took this to
  21913. mean that your program was to be compiled as a C program. Inside the
  21914. iostream.h file are several C++ syntactic constructs (such as class). The
  21915. compiler reported error messages for these constructs since they do not exist
  21916. in C. If you had named the program with a .cpp extension, the compiler would
  21917. have cleanly compiled the program. You might have been confused if you
  21918. compiled other C++ programs without errors and therefore you thought there was
  21919. something wrong with this program. Many compilers (such as Borland), have a
  21920. switch that compiles .c files as C++ programs. If this option was set in your
  21921. configuration file for previous programs, then other C++ programs with .c
  21922. extensions would have compiled properly. If you switched around directories or
  21923. reinstalled the compiler, the switch may have been reset.
  21924. There are numerous books out on C. Many are based on your knowledge of other
  21925. languages. My book C Language for Programmers (QED) gives comparisons of C
  21926. constructs with COBOL, PASCAL, PL/1, BASIC, and FORTRAN. My new book C
  21927. Language for COBOL Programmers (QED) presents very detailed comparisons
  21928. between COBOL language statements and C. All on C (Harper Collins) explains C
  21929. without comparisons to other languages, but with numerous examples. In the C++
  21930. arena, I suggest C++ Programming and Fundamental Concepts by Anderson and
  21931. Heinze (Prentice-Hall). I use that book as a supplementary book for my C++
  21932. courses.
  21933.  
  21934.  
  21935. User interface
  21936.  
  21937.  
  21938. Q
  21939. I am new to the C language and am having a problem that you might help me
  21940. with. I am writing a football card data base, and need help with the user
  21941. interface. What I am trying to do is let the user of my program enter required
  21942. information without pressing the enter key. I also want the user to be able to
  21943. move from one data entry field to another using the arrow and home keys of the
  21944. key pad. If the user presses the arrow keys the highlighted field would move
  21945. up or down as required, but if a letter key or a number key was pressed, the
  21946. letter or number would be concatenated to the appropriate char string
  21947. variable. The concatenating part I can handle. It's getting the input that's
  21948. giving me the problem. I've tried many different approaches to make this work,
  21949. but still no luck. If you could help me with this problem I would be very
  21950. grateful. I'm a subscriber to the C Users Journal, and will be checking the
  21951. Questions & Answers section to see if you can solve my problem. I am using
  21952. Borland C++ version 3.0 (DOS).
  21953. Randy Jones
  21954. Ruther Glen, VA
  21955. A
  21956. As you have discovered, data-field entry functions are not included in the
  21957. Standard C libraries. Numerous shareware and commercially-available libraries
  21958. will perform the operations you list. For a listing of available shareware
  21959. consult The C User's Group Public Domain Catalog [available for free upon
  21960. request from R&D Publications, 1601 W. 23rd St., Lawrence, KS 66046. Ph:
  21961. (913)-841-1631. Fax: (913)-841-2624. e-mail: cujsub@rdpub.com. Perusing The C
  21962. Users' Journal itself will reveal a number of the major vendors of display
  21963. packages. Since for the past year I have been programming almost exclusively
  21964. in Microsoft Windows using Visual C++, I haven't kept up with all the
  21965. different features of the commercial packages. Many of these packages have
  21966. integrated screen designers/code generators. With such a system, you can
  21967. layout your screens with a mouse or cursor keys, rather than by writing
  21968. individual function calls for each field. In case you can't find what you want
  21969. in either the catalog or the ads, my book All on C shows a sample
  21970. implementation of a package of display functions providing most of the
  21971. features you requested.
  21972.  
  21973. Listing 1 One way to do run-time type checking
  21974. void Account::entry_post(Entry * pentry)
  21975. {
  21976. switch(type_of(pentry))
  21977. {
  21978. case Deposit_entry:
  21979. balance += pentry->amount;
  21980. break;
  21981. case Check_entry:
  21982. balance -= pentry->amount;
  21983. break;
  21984. case Withdrawal_entry:
  21985. balance -= pentry->amount;
  21986. break;
  21987. }
  21988. /* End of File */
  21989.  
  21990.  
  21991. Listing 2 Virtual functions simplify run-time type checking
  21992. void Account:: entry_post(Entry * pentry)
  21993. {
  21994. balance = pentry->post(balance);
  21995. }
  21996. /* End of File */
  21997.  
  21998.  
  21999. Listing 3 Code that one compiler didn't like
  22000. /* GRM-P45.C -- Graham, Learning C++, p. 45 */
  22001. // File rabbits.ccp
  22002. // Program for inverse Fibonacci rabbit problem
  22003.  
  22004. #include <iostream.h>
  22005.  
  22006. main()
  22007. {
  22008. // Get input from user
  22009.  
  22010. long current; // number of pairs this month
  22011. long fertile; // number of fertile pairs
  22012. long needed; // number of pairs needed
  22013. ...
  22014. }
  22015.  
  22016. Error messages:
  22017.  
  22018.  
  22019. Compiling C:\GRM-P45.C:
  22020. Error E:\TC\INCLUDE\IOSTREAM.H 38: Declaration syntax error
  22021. Error E:\TC\INCLUDE\IOSTREAM.H 39: Declaration syntax error
  22022. Error E:\TC\INCLUDE\IOSTREAM.H 42: Declaration syntax error
  22023. ...
  22024. // End of File
  22025.  
  22026.  
  22027.  
  22028.  
  22029.  
  22030.  
  22031.  
  22032.  
  22033.  
  22034.  
  22035.  
  22036.  
  22037.  
  22038.  
  22039.  
  22040.  
  22041.  
  22042.  
  22043.  
  22044.  
  22045.  
  22046.  
  22047.  
  22048.  
  22049.  
  22050.  
  22051.  
  22052.  
  22053.  
  22054.  
  22055.  
  22056.  
  22057.  
  22058.  
  22059.  
  22060.  
  22061.  
  22062.  
  22063.  
  22064.  
  22065.  
  22066.  
  22067.  
  22068.  
  22069.  
  22070.  
  22071.  
  22072.  
  22073.  
  22074.  
  22075.  
  22076.  
  22077.  
  22078.  
  22079.  
  22080.  
  22081. Stepping Up To C++ 
  22082.  
  22083.  
  22084. The Return Types of Virtual Functions
  22085.  
  22086.  
  22087.  
  22088.  
  22089. Dan Saks
  22090.  
  22091.  
  22092. Dan Saks is the president of Saks & Associates, which offers consulting and
  22093. training in C++ and C. He is secretary of the ANSI and ISO C++ committees. Dan
  22094. is coauthor of C++ Programming Guidelines, and codeveloper of the Plum Hall
  22095. Validation Suite for C++ (both with Thomas Plum). You can reach him at 393
  22096. Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601, or
  22097. electronically at dsaks@wittenberg.edu.
  22098.  
  22099.  
  22100. WG21+X3J16, the joint ISO ANSI C++ technical committee, is now in its fifth
  22101. year of work on a standard definition for the C++ programming language and its
  22102. accompanying library. Over the years, the committee has added more than a
  22103. dozen new features to the language. I described several of them:
  22104. templates
  22105. exception handling
  22106. new keywords and digraphs to support European translation environments
  22107. wchar_t as a keyword
  22108. function and operator overloading for enumeration types
  22109. operator new[] and operator delete[] for array allocation
  22110. in "Recent Language Extensions to C++", CUJ, June 1993, and described one
  22111. other:
  22112. forward declaration of nested classes in "Nested Classes", CUJ, July 1993.
  22113. This month, I'll explain another extension:
  22114. relaxed restrictions on the return types for virtual functions
  22115. This feature enhances the language's support for object-oriented programming.
  22116. In particular, it extends the set of valid conversions within a type hierarchy
  22117. that you can write without casting. I assume you are familiar with virtual
  22118. function in C++, which I described in my last three columns (see "Virtual
  22119. Functions," "How Virtual Functions Work," and "Overloading and Overriding" in
  22120. CUJ, December, 1993 through February, 1994).
  22121.  
  22122.  
  22123. What the ARM Says
  22124.  
  22125.  
  22126. According to the The Annotated C++ Reference Manual (ARM) (Ellis and
  22127. Stroustrup [1990]), a member function declared in a derived class D overrides
  22128. a virtual function in its base class B only if that function in D has the same
  22129. name and signature (sequence of parameter types) as the function it overrides.
  22130. Clearly, if the name of a function, say f, declared in D differs from the name
  22131. of every function declared in B, then f doesn't override anything. f's
  22132. declaration adds a new name to the scope of D. If D declares a function with
  22133. the same name, again say f, as one or more functions inherited from B, but D's
  22134. f has a signature that differs from the signatures of every f function in B,
  22135. then again D's f doesn't override anything. In this case, D's f hides all of
  22136. B's f functions while in the scope of D. This is not an error, but as I
  22137. explained last month, the resulting behavior might surprise you. Consequently,
  22138. many C++ compilers issue a warning when this sort of hiding occurs.
  22139. What about differing return types? According to Section 10.2 of the ARM: "It
  22140. is an error for a derived class function to differ from a base class virtual
  22141. function in the return type only." For example, in
  22142. class B
  22143. {
  22144. public:
  22145. virtual int vf(int);
  22146. ...
  22147. };
  22148.  
  22149. class D : public B
  22150. {
  22151. ...
  22152. void vf(int); // error
  22153. ...
  22154. };
  22155. the declaration of D::vf is an error because it has the same name and
  22156. signature as a function declared in B, but B::vF and D::vF have different
  22157. return types.
  22158. Consider the consequences of allowing different return types in this
  22159. situation. A function g that accepts a formal parameter of type B * or B & can
  22160. apply vf to that B object, as in
  22161. void g(B *bp)
  22162. {
  22163. ....
  22164. if (bp->vf(0) > 1)
  22165. ...
  22166. }
  22167. When compiling this function, the compiler considers only the definition for
  22168. class B. None of the classes derived from B need to be declared when compiling
  22169. g. Based on the static type of B::vf, the call bp->vf(0) in g should be just
  22170. fine; it returns an int, as the if statement in g apparently expects.
  22171. Since D is publicly derived from B, you can pass a D * to g, as in
  22172. D d;
  22173. ...
  22174.  
  22175. g(&d);
  22176. But now if D::vf has a void return, how can the call bp->vf(0) possibly return
  22177. an int? It can't, which is why the ARM insists that an overriding virtual
  22178. function must have the same return type as the function it overrides.
  22179.  
  22180.  
  22181. Cloning Objects
  22182.  
  22183.  
  22184. Some members of the standards committee suggested that this rule is more
  22185. restrictive than it needs to be. There are, in fact, legitimate circumstances
  22186. where the return types of the overridden and overriding functions need not be
  22187. absolutely identical. Clone functions are one such circumstance.
  22188. Some applications need to be able to clone an object, that is, create an
  22189. object that's an exact copy of another object. Typically, you implement a
  22190. class X with a cloning function as something like
  22191. class X
  22192. {
  22193. public:
  22194. X *clone() const
  22195. { return new X(*this); }
  22196. ...
  22197. };
  22198. Inside X::clone, *this is an expression of type X that designates the object
  22199. being cloned. The expression new X(*this) allocates a new X object and
  22200. initializes it using X's copy constructor (the X constructor that takes an
  22201. argument of type X). clone is a const member function because it does not
  22202. alter *this, and should thus apply to const as well as non-const objects.
  22203. new X returns an X *. Although you could write a clone function that returns
  22204. an X or an X &, I suggest returning X * to emphasize that clone returns a
  22205. dynamically-allocated object that should be deleted eventually. In general,
  22206. for any class X, the return type of X::clone should be X *. For example, in a
  22207. library of geometric shapes, circle::clone should return a circle * and
  22208. rectangle::clone should return a rectangle *.
  22209. When used in a class that's the root of a polymorphic hierarchy, clone
  22210. functions should be virtual, and often pure virtual. This lets you clone an
  22211. object without knowing its exact type. For example, Listing 1 shows a
  22212. polymorphic hierarchy of shapes similar to the one I presented three months
  22213. ago (see "Virtual Functions", CUJ, December 1993). Classes circle, rectangle,
  22214. and triangle are all derived from base class shape. shape declares a pure
  22215. virtual clone function as:
  22216. virtual shape *clone() const = 0;
  22217. Each of the derived classes overrides the clone function with an impure
  22218. definition. For instance, class circle declares
  22219. virtual shape *clone() const;
  22220. in the class definition, and later defines
  22221. shape *circle::clone() const
  22222. {
  22223. return new circle(*this);
  22224. }
  22225. Not long ago I said that, in general, a clone function for a class X should
  22226. have a return type of X *. But here the return type of circle::clone is shape
  22227. *, not circle *. According to ARM, circle::clone must return a shape * because
  22228. that is the return type of the function it overrides. Nonetheless, this clone
  22229. function is still quite useful.
  22230. circle::clone returns the result of new circle, which is indeed a circle *.
  22231. Since circle is publicly derived from shape, a circle is a shape, so the
  22232. circle * in the return expression converts safely and quietly to the return
  22233. type shape *. A similar conversion occurs in the clone functions for rectangle
  22234. and triangle shown in Listing 1. An application can clone an arbitrary shape
  22235. with code such as:
  22236. shape *s;
  22237. ...
  22238. shape *cs = s->clone();
  22239. which leaves cs pointing to an object that has the same dynamic type and value
  22240. as *s.
  22241. Listing 2 shows a more elaborate example dealing with collections of shapes
  22242. implemented as arrays of pointers. The clone_all function replicates an entire
  22243. collection of shapes. First, it allocates a new array to hold the pointers to
  22244. the shape clones. Then, for each shape in the original collection, it clones
  22245. that shape (using its virtual clone function) and places the pointer to the
  22246. copy into the new array. As is always the case with polymorphic objects, it
  22247. doesn't matter to clone_all how many different shapes there are; each shape
  22248. knows how to clone itself.
  22249.  
  22250.  
  22251. Unnecessary Downcasting?
  22252.  
  22253.  
  22254. The ARM's requirement that the return type of an overriding function must be
  22255. the same as the return type of the function it overrides apparently doesn't
  22256. pose any problems when dealing with pointers (or references) to objects at the
  22257. root of the hierarchy, as in Listing 2. However, it often necessitates using
  22258. casts when dealing with pointers to objects of other types in the hierarchy.
  22259. For example, you cannot write
  22260. rectangle *r;
  22261. ...
  22262. rectangle *cr = r->clone();
  22263. because r->clone() returns a shape *. Even though a rectangle is a shape, a
  22264. shape is not necessarily a rectangle. Therefore, you must add a cast, as in
  22265. rectangle *cr = (rectangle *)r->clone();
  22266. Casts are dangerous things. They tell the compiler to stop complaining and let
  22267. you do what you want to do, or at least, what you think you want to do. But
  22268. compilers are right more often than we care to admit. Casts indicate that you
  22269. are doing something that is generally unsafe. Thus, you really should avoid
  22270. casts in your C++ programs, probably even more so than in C programs. When
  22271. casts are rare, the few casts you really do need will stand out and draw more
  22272. scrutiny, which they deserve. (For more about avoiding casts, see Plum and
  22273. Saks [1991].)
  22274. Of course, you don't really need a cast in the previous example, because you
  22275. don't really need to call clone to replicate a shape that you know is a
  22276. rectangle. Rather, you can simply clone *r with
  22277. rectangle *cr = new rectangle(*r);
  22278. But consider what happens if rectangle and triangle (and any other polygons)
  22279. are derived from an abstract base class polygon derived from shape, rather
  22280. than directly from shape, as outlined in Listing 3. (An abstract base class is
  22281. a class with at least one pure virtual function. An abstract base class can be
  22282. a derived class.)
  22283. To satisfy the ARM, all the clone functions in Listing 3 return a shape *.
  22284. (The declaration of polygon's clone function is inside a comment because you
  22285. don't really need it. A function declared pure virtual in a base class remains
  22286. pure virtual in the derived class unless overridden with an impure
  22287. declaration.) When you clone a polygon, you get a pointer of type shape *,
  22288. even though you know that pointer specifically addresses a polygon. Thus, you
  22289. cannot clone a polygon and copy the result to a polygon * without a cast. That
  22290. is, you cannot omit the cast in
  22291. polygon *p;
  22292. ...
  22293. polygon *cp = (polygon *)p->clone();
  22294. All of the casts in the previous examples cast pointers to base class objects
  22295. into pointers to derived class objects. These casts are commonly called
  22296. "downcasts" because most people draw class hierarchies with the base classes
  22297. above their derived classes. Remember, when a class D is publicly derived from
  22298. B, a D is a B, so you can safely convert a D * to a B * without a cast. But a
  22299. B * is not necessarily a D *, so you can't convert a B * downward to a D *
  22300. without a cast. Thus, like all other casts, downcasts are generally unsafe
  22301. unless you're absolutely, positively sure that your B * actually points to a
  22302. D.
  22303. But the downcast in
  22304. polygon *cp = (polygon *)p->clone();
  22305. is actually quite safe because we do know that p->clone() returns a polygon *.
  22306. In fact, there's a whole family of similarly safe downcasts that occur
  22307. commonly in object-oriented systems. The problem is that, on the surface, the
  22308. safe casts look just like the unsafe ones. The cure is to augment the rules
  22309. for virtual overriding so that you can write the safe conversions without
  22310. casts.
  22311. For example, you should be able to declare each virtual clone function in a
  22312. hierarchy so that it returns a pointer whose static type is the same as its
  22313. dynamic type. That is, circle::clone should return a circle * and
  22314. rectangle::clone should return a rectangle *, even though shape::clone returns
  22315. a shape *. Then you can clone any shape, or anything derived from shape,
  22316. without a cast. For instance, given
  22317. shape *s;
  22318.  
  22319. circle *c;
  22320. you can write
  22321. shape *cs = s->clone();
  22322. to clone a shape, and
  22323. circle *cc = c->clone();
  22324. to clone a circle.
  22325. In a sense, declaring circle::clone to return a circle * doesn't introduce any
  22326. new conversions. It merely shifts the exact point where the conversions occur,
  22327. or eliminates the conversions altogether. For example, when you write
  22328. circle::clone as
  22329. shape *circle::clone() const
  22330. {
  22331. return new circle(*this);
  22332. }
  22333. a conversion from circle * to shape * occurs as part of the return statement.
  22334. Then there's no conversion at all in a calling expression like
  22335. shape *s;
  22336. ...
  22337. shape *cs = s->clone();
  22338. In contrast, when you write circle::clone as
  22339. circle *circle::clone() const
  22340. {
  22341. return new circle(*this);
  22342. }
  22343. no conversion occurs inside the function, but an implicit conversion from
  22344. circle *(or rectangle *or triangle *) to shape * occurs in the calling
  22345. expression. The net effect is the same. 
  22346.  
  22347.  
  22348. The New, Relaxed Rules
  22349.  
  22350.  
  22351. The C++ standards committee agreed that the ARM's requirement on the return
  22352. type of virtual functions is a bit too restrictive. Thus, the current draft of
  22353. the Working Paper (the standard-to-be) relaxes the original rule. The new rule
  22354. as it appears in the Working Paper is jargon-rich and seems to change with
  22355. each new draft, so I'll spare you the exact words. Here's more-or-less what it
  22356. says:
  22357. For all classes B and D defined as
  22358. class B
  22359. {
  22360. ...
  22361. virtual BT f();
  22362. ...
  22363. };
  22364.  
  22365. class D : public B
  22366. {
  22367. ...
  22368. DT f();
  22369. ...
  22370. };
  22371. types BT and DT must be identical, or they must satisfy either of the
  22372. following conditions:
  22373. 1. BT is BB * and DT is DD *where DD is derived from BB.
  22374. 2. BT is BB & and DT is DD & where DD is derived from BB.
  22375. In either case (1) or (2),
  22376. 3. class D must have the access rights to convert a BB *(or BB &) to a DD *(or
  22377. DD &, respectively).
  22378. In most common applications, BB is a public base class of DD, so D can perform
  22379. the conversions. But, for example, if BB is a private base class of DD then
  22380. the conversions are not valid, and BT and DT will not satisfy condition (3).
  22381. The above rules apply even if D is derived indirectly from B. Or, BB might be
  22382. B and DD might be D. The latter, in fact, is the case with clone functions.
  22383. Listing 4 shows the shape hierarchy of Listing 1 rewritten using the new
  22384. relaxed rules for the return type of virtual functions. For completeness, I've
  22385. included all the member function bodies so you can use them to build and
  22386. execute the test code in Listing 2.
  22387. Although the committee adopted these relaxed rules in March, 1992, I believe
  22388. most vendors have yet to release a C++ compiler that supports them. As of
  22389. early 1994, only two of the six PC-based compilers I own (Borland 4.0 and
  22390. Watcom 9.5) can compile Listing 4 without error.
  22391.  
  22392.  
  22393. cv-qualifiers in Return Types
  22394.  
  22395.  
  22396. According to the current (September 1993) Working Paper, the cv-qualifiers
  22397. (const and volatile) in the return types of the overriding and overridden
  22398. functions need not be identical. My understanding is that the overriding
  22399. function's return type cannot have any cv-qualifiers that are not also in the
  22400. overridden function's return type. Listing 5 shows some examples.
  22401. Class B in Listing 5 declares virtual function f with a return type const BB
  22402. *, but class D overrides it with a function that returns DD * (where DD is
  22403. publicly derived from BB). Hence, if bp is a B * that actually points to a D,
  22404. then
  22405. const BB *bbp = bp->f();
  22406. invokes D::f applied to *b. D::f returns a pointer to a non-const DD object,
  22407. which the expression bp->f() quietly converts to const BB *.
  22408.  
  22409. Pointer conversions that add cv-qualifiers are always safe, but conversions
  22410. that strip off cv-qualifiers are not. Thus, given
  22411. char *cp;
  22412. const char *ccp;
  22413. then
  22414. ccp = cp;
  22415. is safe, but
  22416. cp = ccp;
  22417. is not. Similarly, as you convert derived types to their base types, adding
  22418. cv-qualifiers to pointer types should not make the conversions any less safe.
  22419. Listing 5 also shows that derived class D has a virtual function g returning a
  22420. const DD & that overrides a function returning a const volatile BB &. This is
  22421. also valid. However, if you omit volatile from the return type of B::g, then
  22422. D::f is erroneous.
  22423. None of the compilers I own support this feature yet.
  22424.  
  22425.  
  22426. More to Come
  22427.  
  22428.  
  22429. Over the past two years, the standards committee has added several other new
  22430. features to C++:
  22431. run-time type identification
  22432. mutable members for const objects
  22433. namespaces
  22434. a predefined boolean type
  22435. new syntax for casts
  22436. I will explain them all in upcoming columns.
  22437.  
  22438.  
  22439. Meeting Dates, Etc.
  22440.  
  22441.  
  22442. WG21+X3J16 will meet three times in 1994:
  22443. March 6-11 in San Diego, CA USA
  22444. July 10-15 in Waterloo, Ontario Canada
  22445. November 6-11 in Valley Forge, PA USA
  22446. If all goes as scheduled, the draft standard should be available for public
  22447. review and comment shortly after the July meeting.
  22448. If you would like to participate in the standards process as a member of
  22449. X3J16, contact the vice-chair:
  22450. Josée Lajoie
  22451. IBM Canada Laboratory
  22452. 844 Don Mills Rd.
  22453. North York, Ontario M3C 1V7 Canada
  22454. (416)448-2734
  22455. josee@vnet.ibm.com
  22456. References
  22457. Ellis and Stroustrup [1990]. Margaret A. Ellis and Bjarne Stroustrup. The
  22458. Annotated C++ Reference Manual. Addison- Wesley.
  22459. Plum and Saks [1991]. Thomas Plum and Dan Saks. C++ Programming Guidelines.
  22460. Plum Hall.
  22461.  
  22462. Listing 1 A class hierarchy with virtual cloning functions that have identical
  22463. return type
  22464. //
  22465. // base class 'shape'
  22466. //
  22467. class shape
  22468. {
  22469. public:
  22470. enum palette { BLUE, GREEN, RED };
  22471. shape(palette c);
  22472. virtual double area() const = 0;
  22473. virtual shape *clone() const = 0;
  22474. palette color() const;
  22475. virtual const char *name() const = 0;
  22476. virtual ostream &put(ostream &os) const;
  22477. private:
  22478. palette _color;
  22479.  
  22480. static const char *color_image[RED - BLUE + 1];
  22481. };
  22482.  
  22483. ...
  22484.  
  22485. //
  22486. // class 'circle' derived from 'shape'
  22487. //
  22488. class circle: public shape
  22489. {
  22490. public:
  22491. circle(palette c, double r);
  22492. double area() const;
  22493. shape *clone() const;
  22494. const char *name() const;
  22495. ostream &put(ostream &os) const;
  22496. private:
  22497. double radius;
  22498. };
  22499.  
  22500. shape *circle::clone() const
  22501. {
  22502. return new circle(*this);
  22503. }
  22504.  
  22505. ...
  22506.  
  22507. //
  22508. // class 'rectangle' derived from 'shape'
  22509. //
  22510. class rectangle : public shape
  22511. {
  22512. public:
  22513. rectangle(palette c, double h, double w);
  22514. double area() const;
  22515. shape *clone() const;
  22516. const char *name() const;
  22517. ostream &put(ostream &os) const;
  22518. private:
  22519. double height, width;
  22520. };
  22521.  
  22522. shape *rectangle::clone() const
  22523. {
  22524. return new rectangle(*this);
  22525. }
  22526.  
  22527. ...
  22528.  
  22529. //
  22530. // class 'triangle' derived from 'shape'
  22531. //
  22532. class triangle : public shape
  22533. {
  22534. public:
  22535. triangle(palette c, double s1, double s2, double a);
  22536. double area() const;
  22537. shape *clone() const;
  22538. const char *name() const;
  22539.  
  22540. ostream &put(ostream &os) const;
  22541. private:
  22542. double side1, side2, angle;
  22543. };
  22544.  
  22545. shape *triangle::clone() const
  22546. {
  22547. return new triangle(*this);
  22548. }
  22549.  
  22550. ...
  22551.  
  22552. // End of File
  22553.  
  22554.  
  22555. Listing 2 A crude application of the shape hierarchy in Listing 1
  22556. //
  22557. // 'clone_all' clones shape array 'sal' with 'n'
  22558. // elements
  22559. //
  22560. shape **clone_all(shape *sa1[], size_t n)
  22561. {
  22562. shape **sa2 = new shape *[n];
  22563. for (size_t i = 0; i < n; ++i)
  22564. sa2[i] = sa1[i]->clone();
  22565. return sa2;
  22566. }
  22567.  
  22568. //
  22569. // 'largest' returns the shape with the largest
  22570. // area from shape array 'sa' with 'n' elements
  22571. //
  22572. shape *largest(shape *sa[], size_t n)
  22573. {
  22574. shape *s = 0;
  22575. double m = 0;
  22576. for (size_t i = 0; i < n; ++i)
  22577. if (sa[i]->area() > m)
  22578. {
  22579. m = sa[i]->area();
  22580. s = sa[i];
  22581. }
  22582. return s;
  22583. }
  22584.  
  22585. int main()
  22586. {
  22587. const int N = 4;
  22588. shape *s[N];
  22589. shape *ls;
  22590. s[0] = new circle(shape::RED, 2);
  22591. s[1] = new triangle(shape::BLUE, 5, 6, asin(0.8));
  22592. s[2] = new rectangle(shape::RED, 3, 4);
  22593. s[3] = new circle(shape::GREEN, 3);
  22594. cout << "The shapes are:\n";
  22595. for (int i = 0; i < N; ++i)
  22596. cout << i << ")\t" << *s[i] << '\n';
  22597. cout << '\n';
  22598. shape **cs = clone_all(s, N);
  22599.  
  22600. cout << "The cloned shapes are:\n";
  22601. for (i = 0; i < N; ++i)
  22602. cout << i << ")\t" << *cs[i] << '\n';
  22603. cout << '\n';
  22604. ls = largest(cs, N);
  22605. cout << "The shape with the largest area is a...\n\t";
  22606. cout << *ls << ".\n";
  22607. cout << "Its area is "<< ls->area() << ".\n";
  22608. return 0;
  22609. }
  22610.  
  22611. // End of File
  22612.  
  22613.  
  22614. Listing 3 The shape hierarchy with rectangle and triangle derived from
  22615. abstract base class polygon
  22616. class shape
  22617. {
  22618. public:
  22619. ...
  22620. virtual shape *clone() const = 0;
  22621. ...
  22622. };
  22623.  
  22624. ...
  22625.  
  22626. class circle : public shape
  22627. {
  22628. public:
  22629. shape *clone() const;
  22630. ...
  22631. };
  22632.  
  22633. shape *circle::clone() const
  22634. {
  22635. return new circle(*this);
  22636. }
  22637.  
  22638. ...
  22639.  
  22640. class polygon : public shape
  22641. {
  22642. public:
  22643. // shape *clone() const = 0; // still pure
  22644. ...
  22645. };
  22646.  
  22647. ...
  22648.  
  22649. class rectangle : public polygon
  22650. {
  22651. public:
  22652. shape *clone() const;
  22653. ...
  22654. };
  22655.  
  22656. shape *rectangle::clone() const
  22657. {
  22658. return new rectangle(*this);
  22659. }
  22660.  
  22661.  
  22662. ...
  22663.  
  22664. class triangle : public polygon
  22665. {
  22666. public:
  22667. shape *clone() const;
  22668. ...
  22669. };
  22670.  
  22671. shape *triangle::clone() const
  22672. {
  22673. return new triangle(*this);
  22674. }
  22675.  
  22676. ...
  22677.  
  22678. // End of File
  22679.  
  22680.  
  22681. Listing 4 The shape class hierarchy with virtual cloning functions employing
  22682. the relaxed return type rules
  22683. #include <iostream.h>
  22684. #include <math.h>
  22685. #include <stddef.h>
  22686.  
  22687. //
  22688. // base class 'shape'
  22689. //
  22690. class shape
  22691. {
  22692. public:
  22693. enum palette { BLUE, GREEN, RED };
  22694. shape(palette c);
  22695. virtual double area() const = 0;
  22696. virtual shape *clone() const = 0;
  22697. palette_color() const;
  22698. virtual const char *name() const = 0;
  22699. virtual ostream &put(ostream &os) const;
  22700. private:
  22701. palette_color;
  22702. static const char *color_image[RED - BLUE + 1];
  22703. };
  22704.  
  22705. const char *shape::color_image[shape::RED - shape::BLUE + 1] =
  22706. { "blue", "green", "red" };
  22707.  
  22708. shape::shape(palette c) : _color(c) { }
  22709.  
  22710. ostream &shape::put(ostream &os) const
  22711. {
  22712. return os << color_image[color()] << ' ' << name();
  22713. }
  22714.  
  22715. shape::palette shape::color() const
  22716. {
  22717. return_color;
  22718. }
  22719.  
  22720. //
  22721.  
  22722. // class 'circle' derived from 'shape'
  22723. //
  22724. const double pi = 3.1415926;
  22725.  
  22726. class circle : public shape
  22727. {
  22728. public:
  22729. circle(palette c, double r);
  22730. double area() const;
  22731. circle *clone() const;
  22732. const char *name() const;
  22733. ostream &put(ostream &os) const;
  22734. private:
  22735. double radius;
  22736. };
  22737.  
  22738. circle::circle(palette c, double r) : shape(c), radius(r) { }
  22739.  
  22740. double circle::area() const
  22741. {
  22742. return pi * radius * radius;
  22743. }
  22744.  
  22745. const char *circle::name() const
  22746. {
  22747. return "circle";
  22748. }
  22749.  
  22750. ostream &circle::put(ostream &os) const
  22751. {
  22752. return shape::put(os) << "with radius = " << radius;
  22753. }
  22754.  
  22755. circle *circle::clone() const
  22756. {
  22757. return new circle(*this);
  22758. }
  22759.  
  22760. //
  22761. // class 'rectangle' derived from 'shape'
  22762. //
  22763. class rectangle: public shape
  22764. {
  22765. public:
  22766. rectangle(palette c, double h, double w);
  22767. double area() const;
  22768. rectangle *clone() const;
  22769. const char *name() const;
  22770. ostream &put(ostream &os) const;
  22771. private:
  22772. double height, width;
  22773. };
  22774.  
  22775. rectangle::rectangle(palette c, double h, double w)
  22776. : shape(c), height(h), width(w) { }
  22777.  
  22778. double rectangle::area() const
  22779. {
  22780. return height * width;
  22781.  
  22782. }
  22783.  
  22784. const char *rectangle::name() const
  22785. {
  22786. return "rectangle";
  22787. }
  22788.  
  22789. ostream &rectangle::put(ostream &os) const
  22790. {
  22791. return shape::put(os) << "with height = " << height
  22792. << " and width = " << width;
  22793. }
  22794.  
  22795. rectangle *rectangle::clone() const
  22796. {
  22797. return new rectangle(*this);
  22798. }
  22799.  
  22800. //
  22801. // class 'triangle' derived from 'shape'
  22802. //
  22803. class triangle : public shape
  22804. {
  22805. public:
  22806. triangle(palette c, double s1, double s2, double a);
  22807. double area() const;
  22808. triangle *clone() const;
  22809. const char *name() const;
  22810. ostream &put(ostream &os) const;
  22811. private:
  22812. double side1, side2, angle;
  22813. };
  22814.  
  22815. triangle::triangle(palette c, double s1, double s2, double a)
  22816. : shape(c), side1(s1), side2(s2), angle(a) { }
  22817.  
  22818. double triangle::area() const
  22819. {
  22820. return side1 * sin(angle) * side2 / 2;
  22821. };
  22822.  
  22823. const char *triangle::name() const
  22824. {
  22825. return "triangle";
  22826. }
  22827.  
  22828. ostream &triangle::put(ostream &os) const
  22829. {
  22830. return shape::put(os) <<" with one side =" << side1
  22831. << ", another side =" << side2
  22832. <<" and angle = " << angle;
  22833. }
  22834.  
  22835. triangle *triangle::clone() const
  22836. {
  22837. return new triangle(*this);
  22838. }
  22839.  
  22840. ostream &operator<<(ostream &os, const shape &s)
  22841.  
  22842. {
  22843. return s.put(os);
  22844. }
  22845.  
  22846. // End of File
  22847.  
  22848.  
  22849. Listing 5 A derived class whose virtual functions have fewer cv-qualifiers
  22850. than the functions they override
  22851. class B
  22852. {
  22853. ...
  22854. virtual const BB *f();
  22855. virtual const volatile BB &g();
  22856. ...
  22857. };
  22858.  
  22859. class D: public B 
  22860. {
  22861. ...
  22862. DD *f();
  22863. const DD &g();
  22864. ...
  22865. };
  22866.  
  22867. // End of File
  22868.  
  22869.  
  22870.  
  22871.  
  22872.  
  22873.  
  22874.  
  22875.  
  22876.  
  22877.  
  22878.  
  22879.  
  22880.  
  22881.  
  22882.  
  22883.  
  22884.  
  22885.  
  22886.  
  22887.  
  22888.  
  22889.  
  22890.  
  22891.  
  22892.  
  22893.  
  22894.  
  22895.  
  22896.  
  22897.  
  22898.  
  22899.  
  22900.  
  22901.  
  22902.  
  22903.  
  22904.  
  22905.  
  22906. Code Capsules
  22907.  
  22908.  
  22909. The Preprocessor
  22910.  
  22911.  
  22912.  
  22913.  
  22914. Chuck Allison
  22915.  
  22916.  
  22917. Chuck Allison is a regular columnist with CUJ and a software architect for the
  22918. Family History Department of the Church of Jesus Christ of Latter Day Saints
  22919. Church Headquarters in Salt Lake City. He has a B.S. and M.S. in mathematics,
  22920. has been programming since 1975, and has been teaching and developing in C
  22921. since 1984. His current interest is object-oriented technology and education.
  22922. He is a member of X3J16, the ANSI C++ Standards Committee. Chuck can be
  22923. reached on the Internet at allison@decus.org, or at (801)240-4510.
  22924.  
  22925.  
  22926. To use C effectively you really have to master two languages: the C language
  22927. proper and the preprocessor. Before a compiler begins the usual chores of
  22928. syntax checking and instruction translation, it submits your program to a
  22929. preliminary phase called preprocessing, which alters the very text of the
  22930. program according to your instructions. The altered text that the compiler
  22931. sees is called a translation unit. In particular, the preprocessor performs
  22932. the following three functions for you:
  22933. 1) header/source file inclusion
  22934. 2) macro expansion
  22935. 3) conditional compilation
  22936. In this article I will illustrate these features of the preprocessor.
  22937.  
  22938.  
  22939. The Include Directive
  22940.  
  22941.  
  22942. One of the first source lines any C programmer sees or composes is this:
  22943. #include <stdio.h>
  22944. Take a moment right now and jot down everything you know about this statement.
  22945. Let's see how you did. stdio.h is of course a standard library header, so
  22946. called because such include directives usually appear near the beginning of a
  22947. source file so that their definitions will be in force throughout the rest of
  22948. the compilation. We commonly think of it as a header file, but there is no
  22949. requirement that the definitions and declarations pertaining to standard input
  22950. and output reside in a file. The C Standard only requires that these
  22951. definitions and declarations replace the include directive in the text of the
  22952. program before translation. They could reside in tables internal to the
  22953. preprocessor. Most compiler implementations do supply header files for the
  22954. standard library, however. MS-DOS compilers install header files in a suitable
  22955. subdirectory. Here is a sampling:
  22956. \BC4\INCLUDE /* Borland C++ 4.0 */
  22957. \MSVC\INCLUDE /* Microsoft Visual C++ */
  22958. \WATCOM\H /* Watcom C/C++ */
  22959. On UNIX systems you will find header files in /usr/include.
  22960. Since an implementation is not even obliged to supply headers in the form of
  22961. physical files, it's no surprise that those implementations providing files
  22962. don't always give them the same name as the include directive. After all, how
  22963. could a compiler supply a file named stdio.h on a platform whose file system
  22964. didn't allow periods in a file name? On MS-DOS systems there can be no file
  22965. that exactly matches the C++ header <strstream.h>, because the file system
  22966. only allows up to eight characters before the period.
  22967. Most MS-DOS implementations map header names into file names by truncating the
  22968. base part (the portion before the period) to eight characters, and the
  22969. extension to three (so the definitions for <strstream.h> reside in the file
  22970. STRSTREA.H). A standard-conforming implementation must supply a mapping to the
  22971. local file system for user-defined header names having at least six characters
  22972. before the period and one character after.
  22973. Conforming compilers also support include directives with string-like
  22974. arguments, as in:
  22975. #include "mydefs.h"
  22976. The string must represent a name recognized by the local file system. The file
  22977. must be a valid C/C++ source file and, like the standard headers, usually
  22978. contains function prototypes, macro definitions, and other declarations. An
  22979. implementation must specify the mechanism it uses to locate the requested
  22980. source file. On platforms with hierarchical file systems, the compiler usually
  22981. searches the current directory first. If that fails, it then searches the
  22982. subdirectory reserved for the standard headers. Because standard header names
  22983. are special preprocessing tokens and not strings, any backslashes in a header
  22984. name are not interpreted as escape characters. In the following directive, a
  22985. double backslash is not needed.
  22986. #include <sys\stat.h> /* \, not \\ */
  22987. #include "\project\include\mydefs.h"
  22988. Included files may themselves contain other include directives, nested up to
  22989. eight levels deep. Since some definitions (like typedefs) must only appear
  22990. once during a compilation, you must guard against the possibility of a file
  22991. being included more than once. The customary technique defines a symbol
  22992. associated with the file. Exclude the text of the file from the compilation if
  22993. the symbol has already been seen by the compiler, as in the following:
  22994. /* mydefs.h */
  22995. #ifndef MYDEFS_H
  22996. #define MYDEFS_H
  22997.  
  22998. <declarations/definitions go here>
  22999.  
  23000. #endif
  23001.  
  23002.  
  23003. Macros
  23004.  
  23005.  
  23006. As you can see, there's more to the #include directive than meets the eye. C
  23007. provides eleven other preprocessor directives you can use to alter your source
  23008. text in meaningful ways (see Table 1). (All begin with the '#' character,
  23009. which must be the first non-space character on its source line.) In this
  23010. section I elaborate on one of the other directives, the #define directive, to
  23011. introduce a very useful construct called a macro.
  23012. The #define directive creates macro definitions. A macro is a name for a
  23013. sequence of zero or more preprocessing tokens. (Valid preprocessing tokens
  23014. include valid C language tokens such as identifiers, strings, numbers and
  23015. operators; and any single character). For example, the line
  23016. #define MAXLINES 500
  23017. associates the text 500 with the symbol MAXLINES. The preprocessor keeps a
  23018. table of all symbols created by the #define directive, along with the
  23019. corresponding replacement text. Whenever the preprocessor encounters the token
  23020. MAXLINES outside of a quoted string or comment, it replaces MAXLINES with the
  23021. token 500. In later phases of compilation it appears as if you actually typed
  23022. 500 instead of MAXLINES. It is important to remember that this operation
  23023. consists of mere text replacement. No semantic analysis occurs during
  23024. preprocessing.
  23025. A macro without parameters, such as MAXLINES, is sometimes called an
  23026. object-like macro because it defines a program constant that looks like an
  23027. object. Because object-like macros are often constants, it is customary to
  23028. type them in upper case as a hint to the reader. You can also define
  23029. function-like macros with zero or more parameters, as in the following code
  23030. fragment:
  23031.  
  23032. #define beep() putc('\a' ,stderr)
  23033. #define abs(x) ((x) >= 0 ? (x) : (-(x)))
  23034. #define max(x,y) (((x) > (y)) ? (x) : (y))
  23035. There must be no whitespace between the macro name and the first left
  23036. parenthesis. The expression
  23037. abs(-4)
  23038. expands to
  23039. ((-4) >= 0 ? (4) : (-(4)))
  23040. You should always parenthesize macro parameters (such as x) in the replacement
  23041. text. This practice prevents surprises from unexpected precedence in complex
  23042. expressions. For example, if you had used the following naive mathematical
  23043. definition for absolute value:
  23044. x >= 0 ? x : -x
  23045. then the expression abs(a - 1) would expand to
  23046. a - 1 >= 0 ? a - 1 : -a - 1
  23047. which is incorrect when a - 1 < 0 (it should be -(a - 1)).
  23048. Even if you put parentheses around all arguments, you should usually
  23049. parenthesize the entire replacement expression as well to avoid surprises with
  23050. respect to the surrounding text. To see this, define abs() without enclosing
  23051. parens, as in:
  23052. #define abs(x) (x) >= 0 ? (x) : (-x)
  23053. Then abs(a) - 1 expands to
  23054. (a) >= 0 ? (a) : -(a) - 1
  23055. which is incorrect for non-negative values of x.
  23056. It is also dangerous to use expressions with side effects as macro arguments.
  23057. For example, the macro call abs(i++) expands to
  23058. ((-i++) >= 0 ? (i++) : (-(i++)))
  23059. No matter what the value of i happens to be, it gets incremented twice, not
  23060. once, which probably isn't what you had in mind.
  23061.  
  23062.  
  23063. Pre-defined Macros
  23064.  
  23065.  
  23066. Conforming implementations supply the five built-in object-like macros shown
  23067. in Table 2. The last three macros remain constant during the compilation of a
  23068. source file. Any other pre-defined macros that a compiler provides must begin
  23069. with a leading underscore followed by either an uppercase letter or another
  23070. underscore.
  23071. You may not redefine any of these five macros with the #define directive, nor
  23072. remove them with the #undef directive. Most compilers support multiple modes,
  23073. some of which are not standard-conforming. (To guarantee that the sample
  23074. program in Listing 1 will run correctly under Borland C, for example, you need
  23075. to run in "ANSI mode" via the "-A" commandline option.)
  23076. Conforming compilers also provide a function-like macro, assert, which you can
  23077. use to put diagnostics in programs. If its argument evaluates to zero, assert
  23078. prints the argument along with source file name and line number (using
  23079. __FILE__ and _LINE_) to the standard error device and aborts the program (see
  23080. Listing 2). For more information on using the assert macro, see the Code
  23081. Capsule "File Processing, Part 2" in the June 1993 issue of CUJ.
  23082. A compiler is allowed to provide macro versions for any functions in the
  23083. standard library (getc and putc usually come as macros for efficiency). With
  23084. the exception of a handful of required function-like macros (assert, setjmp,
  23085. va_arg, va_end, and va_start), an implementation must also supply true
  23086. function versions for all functions in the standard library. A macro version
  23087. of a library function in effect hides its prototype from the compiler, so its
  23088. arguments are not type-checked during translation. To force the true function
  23089. to be called, remove the macro definition with the #undef directive, as in
  23090. #undef getc
  23091. Alternatively, you can surround the function name in parentheses when you call
  23092. it, as in:
  23093. c = (getc)(stdin);
  23094. There's no danger of this expression matching the macro definition since a
  23095. left parenthesis does not immediately follow the function name.
  23096.  
  23097.  
  23098. Conditional Compilation
  23099.  
  23100.  
  23101. You can selectively include or exclude segments of code with conditional
  23102. directives. For example, you can embed the following excerpt in your code to
  23103. accommodate different syntaxes of the delete operator in earlier versions of
  23104. C++:
  23105. #if VERSION < 3
  23106. delete [strlen(p) + 1] p;
  23107. #else
  23108. delete [] p;
  23109. #endif
  23110. Your compiler probably supplies a macro similar to VERSION (Borland C++
  23111. defines __BCPLUSPLUS__ , Microsoft _MSCVER). The argument of an #if directive
  23112. must evaluate to an integer constant, and obeys the usual C rule of non-zero
  23113. means true, zero false. You cannot use casts or the sizeof operator in such
  23114. expressions.
  23115. C++ implementations also pre-define the macro __cplusplus, which you can use
  23116. to customize your code for mixed C/C++ environments. For example, if you want
  23117. to link with existing C code in a C++ environment, you need to use the extern
  23118. "C" linkage specification (which of course is not valid in a C environment).
  23119. The following excerpt will do the right thing in either environment:
  23120. #ifdef __cplusplus
  23121. extern "C"
  23122. {
  23123. #endif
  23124.  
  23125. <put C declarations here>
  23126.  
  23127. #ifdef __cplusplus
  23128. #endif
  23129. The #if directive is handy when you want to comment out long passages of code.
  23130. You can't just wrap such sections in a single, enclosing comment because there
  23131. are likely to be comments in the code itself (right?), causing the outer
  23132. comment to end prematurely. It is better to enclose the code in question in
  23133. the body of an #if directive that always evaluates to zero:
  23134. #if 0
  23135. <put code to be ignored here>
  23136. #endif
  23137.  
  23138.  
  23139.  
  23140. Preprocessor Operators
  23141.  
  23142.  
  23143. Sometimes you just want to know if a macro is defined, without using its
  23144. value. For example, if you only support two compilers, you might have
  23145. something like the following in your code:
  23146. #if defined _MSCVER
  23147. <put Microsoft-specific statements here>
  23148. #elif defined __BCPLUSPLUS______LINEEND____
  23149. <put Borland-specific statements here>
  23150. #else
  23151. #error Compiler not supported.
  23152. #endif
  23153. defined is one of three preprocessor operators (see Table 3). The defined
  23154. operator evaluates to 1 if its argument is present in the symbol table,
  23155. meaning that the macro was either defined by a previous #define directive or
  23156. the compiler provided it as a built-in macro. The #error directive prints its
  23157. argument as a diagnostic and halts the translator.
  23158. It isn't necessary to assign a value to a macro. For example, to insert debug
  23159. trace code into your program, you can do the following:
  23160. #if defined DEBUG
  23161. fprintf(stderr,"x = %d\n",x);
  23162. #endif
  23163. To define the DEBUG macro, just insert the following statement before the
  23164. first use of the macro:
  23165. #define DEBUG
  23166. The following equivalences are recognized by the preprocessor:
  23167. #if defined X <==> #ifdef X
  23168. #if !defined X <==> #ifndef X
  23169. Using the defined operator is more flexible than the equivalent directives on
  23170. the right because you can combine multiple tests as a single expression, as
  23171. in:
  23172. #if defined _cplusplus && !defined DEBUG
  23173. The operator #, the "stringizing" operator, effectively encloses a macro
  23174. argument in quotes. As the program in Listing 3 illustrates, stringizing can
  23175. be useful for debugging. The trace() macro encloses its arguments in quotes so
  23176. they become part of a printf format statement. For example, the expression
  23177. trace(i,d) becomes
  23178. printf("i" " = %" "d" "\n",i);
  23179. and, after the compiler concatenates adjacent string literals it sees this:
  23180. printf("i = %d\n",i);
  23181. There is no way to build quoted strings like this without the stringizing
  23182. operator because the preprocessor ignores macros inside quoted strings.
  23183. The token-pasting operator, ##, concatenates two tokens together to form a
  23184. single token. The call trace2(1) in Listing 4 is translated into
  23185. trace(x1,d)
  23186. Any space surrounding these two operators is ignored.
  23187.  
  23188.  
  23189. Implementing a s s e r t ( )
  23190.  
  23191.  
  23192. Implementing assert reveals an important fact about using macros. Since the
  23193. action of assert depends on the result of a test, you might first try an if
  23194. statement, as in:
  23195. #define assert(cond) \
  23196. if (!(cond)) __assert(#cond,__FILE__,__LINE__)
  23197. where the function __assert prints the message and halts the program. This
  23198. implementation causes a problem, however, when assert finds itself within an
  23199. if statement, as in:
  23200. if (x > 0)
  23201. assert(x != y)
  23202. else
  23203. /* whatever */
  23204. because the preceding code expands into
  23205. if (x > 0)
  23206. if (!(x != y))_assert("x != y","file.c",7);
  23207. else
  23208. /* whatever */
  23209. The indentation that results from expanding assert in place is misleading
  23210. because it's actually the second if that intercepts the else. Rewriting the
  23211. expanded code to represent the actual flow of control produces:
  23212. if (x > 0)
  23213. if (!(x != y))
  23214. __assert("x != y","file.c",7)
  23215. else /* OOPS! New control flow! */
  23216. /* whatever */
  23217. The usual fix for nested if problems such as this is to use braces, as in:
  23218. #define assert(cond) \
  23219.  
  23220. {if (!(cond))_assert
  23221. (#cond,__FILE,__LINE__) }
  23222. but this code expands into
  23223. if (x > 0)
  23224. {if (!(x != y)) _assert
  23225. ("x != y","file.c",7)};
  23226. else
  23227. /* whatever */
  23228. and the combination }; in the second line creates a null statement that
  23229. completes the outer if, leaving a dangling else, which is a syntax error. A
  23230. correct way to define assert is shown in Listing 5. (This simple version does
  23231. not recognize the macro NDEBUG.) (Listing 6 shows the implementation of the
  23232. support function __assert()). In general, when a macro must make a choice, it
  23233. is good practice to write it as an expression and not as a statement.
  23234.  
  23235.  
  23236. Macro Magic
  23237.  
  23238.  
  23239. It's important to understand precisely what steps the preprocessor follows to
  23240. expand macros, otherwise you can be in for some mysterious surprises. For
  23241. example, if you insert the following line near the beginning of Listing 4:
  23242. #define x1 SURPRISE!
  23243. then trace2(1) expands into
  23244. trace(x ## 1,d)
  23245. which in turn becomes
  23246. trace(x1,d)
  23247. But the preprocessor doesn't stop there. It rescans the line to see if any
  23248. other macros need expanding. The final state of the program text seen by the
  23249. compiler is shown in Listing 7.
  23250. To further illustrate, consider the text in Listing 8. Listing 8 is not a
  23251. complete program, by the way, but is for preprocessing only -- don't try to
  23252. compile it all the way. (If you have Borland C use the CPP command.) The
  23253. output from the preprocessor appears in Listing 9. The str() macro just puts
  23254. quotes around its argument. It might appear that xstr() is redundant, but
  23255. there is an important difference between xstr() and str(). The output of the
  23256. statement str(VERSION) is of course
  23257. "VERSION"
  23258. but xstr(VERSION) expands to
  23259. str(2)
  23260. because arguments not connected with a # or ##are fully expanded before they
  23261. replace their respective parameters. The preprocessor then rescans the
  23262. statement, providing "2". So in effect, xstr() is a version of str() that
  23263. expands its argument before quoting it.
  23264. The same relationship exists between glue() and xglue(). The statement
  23265. glue(VERSION,3) concatenates its arguments into the token VERSION3, but
  23266. xglue(VERSION,3) first expands VERSION, producing
  23267. glue(2,3)
  23268. which in turn rescans into the token 23.
  23269. The next two statements are a little trickier:
  23270. glue(VERS,ION)
  23271. == VERS ## ION
  23272. == VERSION
  23273. == 2
  23274. and
  23275. xglue(VERS,ION)
  23276. == glue(VERS,ATILE)
  23277. == VERS ## ATILE
  23278. == VERSATILE
  23279. Of course, if VERSATILE were a defined macro it would be furher expanded.
  23280. The last four statements in listing 8 expand as follows:
  23281. ID(VERSION)
  23282. == "This is version "xstr(2)
  23283. == "This is version "str(2)
  23284. == "This is version ""2"
  23285.  
  23286. INCFILE(VERSION)
  23287. == xstr(glue(version,2)) ".h"
  23288. == xstr(version2) ".h"
  23289. == "version2" ".h"
  23290.  
  23291. str(INCFILE(VERSION))
  23292. == #INCFILE(VERSION)
  23293. == "INCFILE(VERSION)"
  23294.  
  23295. xstr(INCFILE(VERSION))
  23296. == str("version2" ".h")
  23297. == #"version2" ".h"
  23298. == "\"version2\" \".h\""
  23299.  
  23300. For obvious reasons, the # operator effectively inserts escape characters
  23301. before all embedded quotes and backslashes.
  23302. The macro replacement facilities of the preprocessor clearly offer you an
  23303. incredible amount of flexibility (too much, some would say). There are two
  23304. limitations to keep in mind:
  23305. 1) If at any time the preprocessor encounters the current macro in its own
  23306. replacement text, no matter how deeply nested in the process, the preprocessor
  23307. does not expand it but leaves it as-is (otherwise the process would never
  23308. terminate!). For example, given the definitions
  23309. #define F(f) f(args)
  23310. #define args a,b
  23311. F(g) expands to g(a,b), but what does F(F) expand to? (Answer: F(a,b)).
  23312. 2) If a fully-expanded statement resembles a preprocessor directive, (e.g., if
  23313. expansion results in an #include directive), the directive is not invoked, but
  23314. is left verbatim in the program text. (Thank goodness!).
  23315.  
  23316.  
  23317. Character Sets and Trigraphs
  23318.  
  23319.  
  23320. The character set you use to compose your program doesn't have to be the same
  23321. as the one in which the program executes. These two character sets often
  23322. differ in non-English applications. A C translator only understands the source
  23323. character set -- English alphanumerics, the graphics characters used for
  23324. operators and punctuators (there are 29 of them), and a few control characters
  23325. (newline, horizontal tab, vertical tab, and form-feed). Any other characters
  23326. presented to the translator may appear only in quoted strings, character
  23327. constants, header names or comments. The execution character set is the set of
  23328. characters that the program uses in its literals, and to input and output
  23329. data. This set is implementation-defined, but must at least contain characters
  23330. representing alert ('\a'), backspace ('\b'), carriage return ('\r'), newline
  23331. ('\n'), form feed ('\f'), vertical tab ('\v'), and horizontal tab ('\t').
  23332. Many non-U.S. environments use different graphics for some of the elements of
  23333. the source character set, making it impossible to write readable C programs.
  23334. To overcome this obstacle, standard C defines a number of trigraphs, which are
  23335. triplets of characters from the Invariant Code Set (ISO 646-1983) found in
  23336. virtually every environment in the world. Each trigraph corresponds to a
  23337. character in the source character set which is not in ISO 646 (see Table 4).
  23338. For example, whenever the preprocessor encounters the token ??= anywhere in
  23339. your source text (even in strings), it replaces this token with the '#'
  23340. character code from the source character set. The program in Listing 11 shows
  23341. how to write the "Hello, world!" program from Listing 10 using trigraphs.
  23342. (Borland users: you have a separate executable, trigraph.exe, for procesing
  23343. trigraphs.)
  23344. In an effort to enable more readable programs world-wide, the C++ draft
  23345. standard defines a set of digraphs and new keywords for non-ASCII developers
  23346. (see Table 5). Listing 12 shows what "Hello, world" looks like using these new
  23347. tokens. Perhaps you will agree that the symmetric look of the bracketing
  23348. operators is easier on the eye.
  23349.  
  23350.  
  23351. Phases Of Translation
  23352.  
  23353.  
  23354. The C standard defines eight distinct phases of translation. An implementation
  23355. doesn't make eight separate passes through the code, of course, but the result
  23356. of translation must behave as if it had. The eight phases are:
  23357. 1. Physical source characters are mapped into the source character set. This
  23358. includes trigraph replacement and things like mapping a carriage return/line
  23359. feed to a single newline character in MS-DOS.
  23360. 2. All lines that end in a backslash are merged with their continuation line,
  23361. and the backslash is deleted.
  23362. 3. The source is parsed into preprocessing tokens and comments are replaced
  23363. with a single space character. The C++ digraphs are recognized as tokens.
  23364. 4. Preprocessing directives are invoked and macros are expanded. Steps 1
  23365. through 4 are repeated for any included files.
  23366. 5. Escape sequences in character constants and string literals that represent
  23367. characters in the execution set are converted (e.g., '\a' would be converted
  23368. to a byte value of 7 in an ASCII environment).
  23369. 6. Adjacent string literals are concatenated.
  23370. 7. Traditional compilation occurs: lexical and semantic analysis, and
  23371. translation to assembly or machine code.
  23372. 8. Linking occurs: external references are resolved and a program image is
  23373. made ready for execution.
  23374. The preprocessor performs steps 1 through 4.
  23375.  
  23376.  
  23377. C++ And The Preprocessor
  23378.  
  23379.  
  23380. C++ preprocessing formally differs from that of C only in the tokens it
  23381. recognizes. A C++ preprocessor must recognize the tokens in Table 5 as well as
  23382. .*, ->*, and ::. It must also recognize //-style comments and replace them
  23383. with a single space. Though C++'s preprocessor isn't much different than C's,
  23384. you may want to use it a lot differently. For example, as far as I can tell,
  23385. there is no good reason to define object-like macros anymore. You should use
  23386. const variable definitions instead. The statement
  23387. const int MAXLINES = 500;
  23388. has a couple of advantages over
  23389. #define MAXLINES 500
  23390. Since the compiler knows the semantics of the object, you get stronger
  23391. compile-time type checking. You can also reference const objects like any
  23392. other with a symbolic debugger. Global const objects have internal linkage
  23393. unless you explicitly declare them extern, so you can safely replace all your
  23394. object-like macros with const definitions.
  23395. Function-like macros are almost unnecessary in C++. You can replace most
  23396. function-like macros with inline functions. For example, replace the max macro
  23397. as shown previously with
  23398. inline int max(int x, int y)
  23399. {
  23400. return x >= y ? x : y;
  23401. }
  23402. Note that you don't have to worry about parenthesizing to avoid precedence
  23403. surprises, because this code defines a real function, with scope and type
  23404. checking. You also don't have to worry about side effects like you do with
  23405. macros, such as in the call
  23406. max(x++,y++)
  23407. The macro version may seem superior to the inline function because it accepts
  23408. arguments of any type. No problem. Define max as a template, as in the
  23409. following code; now the inline function will accept arguments of any type:
  23410. template<class T>
  23411. inline int max(const T& x, const T& y)
  23412. {
  23413. return x > y ? x : y;
  23414. }
  23415. Do keep in mind, however, that inline is a only hint to the compiler. Not all
  23416. functions are amenable to inlining, especially those with loops and
  23417. complicated control structures. Your compiler may tell you that it can't
  23418. inline a function. Still, in many cases it is better to define a function
  23419. out-of-line than to define it as a macro and lose the type safety that a real
  23420. function affords.
  23421. There is still room in C++ for function-like macros that use the stringizing
  23422. or token-pasting operators. The program in Listing 13 uses stringizing and an
  23423. inline function to test the new string class available with Borland C++ 4.0.
  23424.  
  23425.  
  23426. Conclusion
  23427.  
  23428.  
  23429.  
  23430. The preprocessor doesn't know C or C++. It is a language all its own. Many
  23431. library vendors have used the preprocessor intelligently to simplify the
  23432. installation and use of their products. I encourage you to use it, but to use
  23433. it prudently. It has some dark corners, which I've purposely avoided. It is
  23434. good practice, especially with C++, to do as much as you can in the
  23435. programming language, and use the preprocessor only when you need to.
  23436. Table 1 Preprocessor directives
  23437. #include Includes text of header or source file.
  23438.  
  23439. #define Enters a symbol into the symbol table for the
  23440.  current compilation unit, with an optional
  23441.  value.
  23442. #undef Removes a symbol from the symbol table.
  23443.  
  23444. #if Control flow directives for conditional
  23445. #elif compilation.
  23446. #else
  23447. #endif
  23448.  
  23449. #ifdef Symbol table query directives.
  23450. #ifndef (ALso used for conditional compilation).
  23451.  
  23452. #line Renumbers the current source line. UtiLities
  23453.  like code generators use this to synchronize
  23454.  generated lines with original source lines in
  23455.  error messages.
  23456.  
  23457. #pragma Compiler-dependent actions.
  23458. Table 2 Pre-defined macros
  23459. Macro Value
  23460. ---------------------------------------------
  23461. __LINE__ The number of the current source
  23462.  line (equal to one more than the
  23463.  number of newline characters read
  23464.  so far).
  23465.  
  23466. __FILE__ The name of the source file.
  23467.  
  23468. __DATE__ The date of translation, in the
  23469.  form "Mmm dd yyyy."
  23470.  
  23471. __TIME__ The time of translation, in the
  23472.  form "hh:mm:ss".
  23473.  
  23474. __STDC__ 1, if the compilation is in
  23475.  "standard" mode.
  23476. Table 3 Preprocessor operators
  23477. Operator Usage
  23478. # Stringizing
  23479. ## Token pasting
  23480. defined Symbol table query
  23481. Table 4 Trigraph sequences
  23482. Trigraph C Source Character
  23483.  ??= #
  23484.  ??( [
  23485.  ??/ \
  23486.  ??) ]
  23487.  ??' ^
  23488.  ??< {
  23489.  ??! 
  23490.  ??> }
  23491.  ??- ~
  23492. Table 5 New C++ digraphs and identifiers
  23493.  
  23494. Token Translation
  23495.  <% {
  23496.  %> }
  23497.  <: [
  23498.  :> ]
  23499.  %% #
  23500.  bitand &
  23501.  and &&
  23502.  bitor 
  23503.  or 
  23504.  xor ^
  23505.  compl ~
  23506.  and_eq &=
  23507.  or_eq =
  23508.  xor_eq ^=
  23509.  not !
  23510.  not_eq !=
  23511.  
  23512. Listing 1 Prints the pre-defined macros
  23513. /* sysmac.c: Print system macros */
  23514.  
  23515. #include <stdio.h>
  23516.  
  23517. main()
  23518. {
  23519. printf("__DATE__ == %s\n",__DATE__);
  23520. printf("__FILE__ == %s\n",__FILE__);
  23521. printf("__LINE__ == %d\n",__LINE__);
  23522. printf("__TIME__ == %s\n",__TIME__);
  23523. printf("__STDC__ == %d\n",__STDC__);
  23524. return 0;
  23525. }
  23526.  
  23527. /* Output:
  23528. __DATE__ == Dec 18 1993
  23529. __FILE__ == sysmac.c
  23530. __LINE__ == 9
  23531. __TIME__ == 19:05:06
  23532. __STDC__ == 1
  23533. */
  23534. /* End of File */
  23535.  
  23536.  
  23537. Listing 2 Illustrates an assertion failure
  23538. /* fail.c */
  23539. #include <stdio.h>
  23540. #include <assert.h>
  23541.  
  23542. main()
  23543. {
  23544. int i = 0;
  23545.  
  23546. assert(i > 0);
  23547. return 0;
  23548. }
  23549.  
  23550. /* Sample Execution:
  23551. C:>assert
  23552. Assertion failed: i > 0,
  23553.  
  23554. file assert.c, line 9
  23555. Abnormal program termination
  23556. */
  23557. /* End of File */
  23558.  
  23559.  
  23560. Listing 3 Illustrates the stringizing operator
  23561. /* trace.c: Illustrate a trace *
  23562. * macro for debugging */
  23563.  
  23564. #include <stdio.h>
  23565.  
  23566. #define trace(x,format) \
  23567. printf(#x " = %" #format "\n",x)
  23568.  
  23569. main()
  23570. {
  23571. int i = 1;
  23572. float x = 2.0;
  23573. char *s = "three";
  23574.  
  23575. trace(i,d);
  23576. trace(x,f);
  23577. trace(s,s);
  23578. return 0;
  23579. }
  23580.  
  23581. /* Output:
  23582. i= 1
  23583. x = 2.000000
  23584. s = three
  23585. */
  23586. /* End of File */
  23587.  
  23588.  
  23589. Listing 4 Illustrate the token-pasting operator
  23590. /* trace2.c: Illustrate a trace *
  23591. * macro for debugging */
  23592.  
  23593. #include <stdio.h>
  23594.  
  23595. #define trace(x,format) \
  23596. printf(#x " = %"#format "\n",x)
  23597. #define trace2(i) trace(x## i,d)
  23598.  
  23599. main()
  23600. {
  23601. int x1 = 1, x2 = 2, x3 = 3;
  23602. trace2(1);
  23603. trace2(2);
  23604. trace2(3);
  23605. return 0;
  23606. }
  23607.  
  23608. /* Output:
  23609. x1 = 1
  23610. x2 = 2
  23611. x3 = 3
  23612. */
  23613.  
  23614. /* End of File */
  23615.  
  23616.  
  23617. Listing 5 A simple implementation of the assert macro
  23618. /* assert.h */
  23619.  
  23620. extern void __assert(char *, char *, long);
  23621.  
  23622. #undef assert
  23623. #ifdef NDEBUG
  23624. #define assert(cond)
  23625. (void) 0
  23626. #else
  23627.  
  23628. #define assert(cond) \
  23629. ((cond) \
  23630. ? (void) 0 \
  23631. : __assert(#cond,__FILE__,__LINE__))
  23632. /* End of File */
  23633.  
  23634.  
  23635. Listing 6 The __assert support function
  23636. /* xassert.c */
  23637. #include <stdio.h>
  23638. #include <stdlib.h>
  23639.  
  23640. void __assert(char *cond, char *fname, long lineno)
  23641. {
  23642. fprintf(stderr,
  23643. "Assertion failed: %s, file %s, line %ld\n",
  23644. cond,fname,lineno);
  23645. abort();
  23646. }
  23647.  
  23648. /* End of File */
  23649.  
  23650.  
  23651. Listing 7 Preprocessed source with a surprise
  23652. main()
  23653. {
  23654. int SURPRISE! = 1, x2 = 2, x3 = 3;
  23655. printf("x1" " = %" "d" "\n",SURPRISE!);
  23656. printf("x2" " = %" "d" "\n",x2);
  23657. printf("x3" " = %" "d" "\n",x3);
  23658. return 0;
  23659. }
  23660. /* End of File */
  23661.  
  23662.  
  23663. Listing 8 Illustrates macro rescanning
  23664. /* preproc.c: Test # and ## preprocessing operators
  23665. *
  23666. * NOTE: DO NOT COMPILE! Preprocess only!
  23667. */
  23668.  
  23669. /* Handy stringizing macros */
  23670. #define str(s) #s
  23671. #define xstr(s) str(s)
  23672.  
  23673.  
  23674. /* Handy token-pasting macors */
  23675. #define glue(a,b) a##b
  23676. #define xglue(a,b) glue(a,b)
  23677.  
  23678. /* Some definitions */
  23679. #define ID(x) "This is version " ## xstr(x)
  23680. #define INCFILE(x) xstr(glue(version,x)) ".h"
  23681. #define VERSION 2
  23682. #define ION ATILE
  23683.  
  23684. /* Expand some macros */
  23685. str(VERSION)
  23686. xstr(VERSION)
  23687. glue(VERSION,3)
  23688. xglue(VERSION,3)
  23689. glue(VERS,ION)
  23690. xglue(VERS,ION)
  23691.  
  23692. /* Expand some more */
  23693. ID(VERSION)
  23694. INCFILE(VERSION)
  23695. str(INCFILE(VERSION))
  23696. xstr(INCFILE(VERSION))
  23697. /* End of File */
  23698.  
  23699.  
  23700. Listing 9 Preprocessed results from Listing 8
  23701. "VERSION"
  23702. "2"
  23703. VERSION3
  23704. 23
  23705. 2
  23706. VERSATILE
  23707.  
  23708. "This is version ""2"
  23709. "version2" ".h"
  23710. "INCFILE(VERSION)"
  23711. "\"version2\" \".h\""
  23712.  
  23713.  
  23714. Listing 10 A "Hello, world!" program
  23715. /* hello.c: Greet either the user or the world *\
  23716. #include <stdio.h>
  23717.  
  23718. main(int argc, char *argv[])
  23719. {
  23720. if (argc > 1 && argv[1] != NULL)
  23721. printf("Hello, %s!\n",argv[1]);
  23722. else
  23723. printf("Hello, world!\n");
  23724. return 0;
  23725. }
  23726.  
  23727. /* End of File */
  23728.  
  23729.  
  23730. Listing 11 "Hello, World!" using trigraphs
  23731. /* thello.c: Greeting program using trigraphs */
  23732. #include <stdio.h>
  23733.  
  23734.  
  23735. main(int argc, char *argv??(??))
  23736. ??<
  23737. if (argc > 1 && argv??(0??) != NULL)
  23738. printf("Hello, %s!??/n",argv??(1??));
  23739. else
  23740. printf("Hello, world!??/n");
  23741. return 0;
  23742. ??>
  23743.  
  23744. /* End of File */
  23745.  
  23746.  
  23747. Listing 12 "Hello, World!" with the new C++ digraphs and tokens
  23748. /* dhello.c: Greeting program using C++ digraphs */
  23749. #include <stdio.h>
  23750.  
  23751. main(int argc, char *argv<::>)
  23752. <%
  23753. if (argc> 1 and argv<:0:> != NULL)
  23754. printf("Hello, %s!??/n",argv<:1:>);
  23755. else
  23756. printf("Hello, world!??/n");
  23757. return 0;
  23758. %>
  23759.  
  23760. /* End of File */
  23761.  
  23762.  
  23763. Listing 13 Uses macros and an inline function to test the standard C++ class
  23764. // tstr.cpp: Test the C++ string class
  23765.  
  23766. #include <iostream.h>
  23767. #include <stddef.h>
  23768. #include <cstring.h>
  23769.  
  23770. // Handy display macros
  23771. #define result(exp) \
  23772. cout << #exp ": \"" << (exp) << '\"' << endl
  23773. #define test(obj,exp) \
  23774. exp, print(#obj ", after "#exp ":\n",obj)
  23775.  
  23776. // Print a string in quotes
  23777. inline void print(const char *p, const string& s)
  23778. {
  23779. cout << p << '"' << s << '"' << endl;
  23780. }
  23781.  
  23782. main()
  23783. {
  23784. string s1("Now is the time for all worthy carbon units"),
  23785. s2 = "to come to the aid of their sector.",
  23786. s3 = '\n',
  23787. s4(s1);
  23788. size_t len = s1.length();
  23789. // Test some operators
  23790. result(s1 == s4);
  23791. result(s1 < s4);
  23792. result(s1 + s3 + s2);
  23793.  
  23794. test(s1,s1 += s3 + s2);
  23795. result(s1 == s4);
  23796. test(s1,s1.resize(len));
  23797. result(s1 == s4);
  23798. cout << endl;
  23799.  
  23800. // Search and replace
  23801. size_t pos = s1.find("all");
  23802. if (pos != NPOS)
  23803. test(s1,s1.replace(pos,3,"some"));
  23804. pos = s1.find("worthy");
  23805. if (pos != NPOS)
  23806. {
  23807. result(s1.substr(pos,5));
  23808. test(s1,s1.insert(pos,"un"));
  23809. }
  23810. cout << endl;
  23811.  
  23812. // More searching
  23813. result(s1.find_first_of("aeiou"));
  23814. result(s1.find_first_not_of("aeiou"));
  23815. result(s1.find_last_of("aeiou"));
  23816. result(s1.find_last_not_of("aeiou"));
  23817. cout << endl;
  23818.  
  23819. // Subscripting
  23820. pos = s2.find_first_of('d');
  23821. test(s2,s2[pos] = 'l');
  23822. return 0;
  23823. }
  23824.  
  23825. /* Output:
  23826. s1 == s4: "1"
  23827. s1 < s4: "0"
  23828. s1 + s3 +s2: "Now is the time for all worthy carbon units
  23829. to come to the aid of their sector."
  23830. s1, after s1 += s3 + s2:
  23831. "Now is the time for all worthy carbon units
  23832. to come to the aid of their sector."
  23833. s1 == s4: "0"
  23834. s1, after s1.resize(len):
  23835. "Now is the time for all worthy carbon units"
  23836. s1 == s4: "1"
  23837.  
  23838. s1, after s1.replace(pos,3,"some"):
  23839. "Now is the time for some worthy carbon units"
  23840. s1.substr(pos,5): "worth"
  23841. s1, after s1.insert(pos,"un"):
  23842. "Now is the time for some unworthy carbon units"
  23843.  
  23844. s1.find_first_of("aeiou"): "1"
  23845. s1.find_first_not_of("aeiou"): "0"
  23846. s1.find_last_of("aeiou"): "43"
  23847. s1.find_last_not_of("aeiou"): "45"
  23848.  
  23849. s2, after s2[pos] = 'l':
  23850. "to come to the ail of their sector."
  23851. */
  23852.  
  23853.  
  23854. // End of File
  23855.  
  23856.  
  23857.  
  23858.  
  23859.  
  23860.  
  23861.  
  23862.  
  23863.  
  23864.  
  23865.  
  23866.  
  23867.  
  23868.  
  23869.  
  23870.  
  23871.  
  23872.  
  23873.  
  23874.  
  23875.  
  23876.  
  23877.  
  23878.  
  23879.  
  23880.  
  23881.  
  23882.  
  23883.  
  23884.  
  23885.  
  23886.  
  23887.  
  23888.  
  23889.  
  23890.  
  23891.  
  23892.  
  23893.  
  23894.  
  23895.  
  23896.  
  23897.  
  23898.  
  23899.  
  23900.  
  23901.  
  23902.  
  23903.  
  23904.  
  23905.  
  23906.  
  23907.  
  23908.  
  23909.  
  23910.  
  23911.  
  23912.  
  23913.  
  23914.  
  23915.  
  23916.  
  23917. CUG New Releases
  23918.  
  23919.  
  23920. IOCCC, ASXXXX, MINED, TDE Update, and a Bug Fix
  23921.  
  23922.  
  23923.  
  23924.  
  23925. Victor R. Volkman
  23926.  
  23927.  
  23928. Victor R. Volkman received a BS in Computer Science from Michigan
  23929. Technological University. He has been a frequent contributor to The C Users
  23930. Journal since 1987. He is currently employed as Senior Analyst at H. C.I.A. of
  23931. Ann Arbor, Michigan. He can be reached by dial-in at the HAL 9000 BBS (313)
  23932. 663-4173 or by Usenet mail to sysop@hal9k.com.
  23933.  
  23934.  
  23935.  
  23936.  
  23937. CUG Library Volume IV
  23938.  
  23939.  
  23940. CUG Library proudly announces volume IV of its directory of user-supported C
  23941. source code. This latest effort thoroughly catalogs CUG Library volume numbers
  23942. #300 through #349. Volume IV includes both exhaustive reviews of selected
  23943. volumes plus capsule summaries of all volumes. In all, this amounts to more
  23944. than 250 cross-referenced and indexed pages of information. Volume IV can be
  23945. yours for $10 or order the set of volumes I through IV for just $28 total. As
  23946. always, see the order blank in the center portion of this issue.
  23947.  
  23948.  
  23949. Bug Fix for GNUPlot, CUG #334
  23950.  
  23951.  
  23952. Arild Olsen <d_olsen_o@kari.uio.no> writes:
  23953. "I just received the disk, and the HPLJII-driver does not function, as stated
  23954. by R.T. Stevens in his review (CUJ, June 93). Maybe this is due to an
  23955. incompatibility between HP LaserJet II and III; I have a LJ III. When sending
  23956. a bitmap to the printer, the program specifies TIFF-format. This is not
  23957. correct since the bitmap is plain.
  23958. To solve this, edit the HPLJIItext function in the HPLJII driver. The string
  23959. "\033*b2m%dW" should be changed to "\033*b0m%dW".
  23960.  
  23961.  
  23962. New Acquisitions and Updates
  23963.  
  23964.  
  23965. This month we present three additions to the CUG Library, as well as an update
  23966. to a recently featured volume.
  23967. International Obfuscated C Code Contest (CUG #397)
  23968. ASxxxx Cross Assembler - Part 3 (CUG #398): MC 68HC08 CPU support
  23969. MINED Editor: (CUG #399)
  23970. Thomson-Davis Editor (CUG #386 update): multi-window text/binary file editor
  23971. -- new version 3.2A
  23972.  
  23973.  
  23974. International Obfuscated C Code Contest 1984-1993: CUG #397
  23975.  
  23976.  
  23977. Landon Noll (Sunnyvale, CA) submits a decade of source code from the
  23978. International Obfuscated C Code Contest (IOCCC). This contest has long been a
  23979. favorite of many CUJ readers. The entire IOCCC archive from 1984-1993 is now
  23980. available as a two-diskette set from the CUG Library. Obfuscation implies
  23981. purposefully obscuring and confusing a situation. Why obfuscate C code? The
  23982. official IOCCC states its objectives as follows:
  23983. To show the importance of programming style, in an ironic way.
  23984. To stress C compilers by feeding them unusual code.
  23985. To illustrate some of the subtleties of the C language.
  23986. To provide a safe forum for poor C code.
  23987. Recently, Bob van der Poel reviewed Don Libes' book entitled Obfuscated C and
  23988. Other Mysteries (see CUJ, October 1993, pp. 131-132). The diskette for this
  23989. book includes IOCCC entries from 1984-1991. Libes has produced special reports
  23990. about the IOCCC several times in CUJ. Please see the following back issues for
  23991. more detail:
  23992. Libes, Don. "Don't Put This on Your Resume," CUJ, May 1991, p. 89.
  23993. Libes, Don. "The Far Side of C," CUJ, May 1990, p. 125.
  23994. Libes, Don. "The International Obfuscated C Code Contest," CUJ, July 1989, p.
  23995. 93.
  23996. The CUG Library volume #397 contains the full IOCCC archive including two
  23997. additional years not included in the Libes' book.
  23998. In addition to dozens and dozens of obfuscated C programs, the archive
  23999. includes complete rules and guidelines so you can submit your own entries into
  24000. next year's contest. Some of the obfuscated programs are quite useful,
  24001. including scaled-down versions of make, grep, and various editors.
  24002.  
  24003.  
  24004. ASxxxx Cross Assembler - Part 3: CUG 398
  24005.  
  24006.  
  24007.  
  24008. Cross assemblers continue to play an important role in the CUG Library. A
  24009. cross assembler reads assembly language source code for a non-native CPU and
  24010. writes object code that can be linked and downloaded to the target machine for
  24011. execution. Embedded systems developers are the most frequent users of cross
  24012. assemblers. This month, Alan R. Baldwin (Kent State University, Ohio), adds
  24013. his third cross assembler to the CUG Library's repetoire. ASxxxx Part 3
  24014. provides a complete Motorola 68HC08 development system. ASxxxx Part 3 version
  24015. 1.50 (released 8/9/93) is immediately available as CUG volume #398.
  24016. The CUG distribution of ASxxx Part 3 includes MS-DOS executables for the
  24017. ASxxxx Cross Assembler and Linker. However, if you want to recompile the Cross
  24018. Assembler or Linker, you'll also need ASxxxx Part 1 (CUG #292). ASxxx Part 2
  24019. contains cross assembler source files for the 6816 CPU. The ASxxxx family of
  24020. cross assemblers can be built on DEC machines running DECUS C in the TSX+
  24021. environment or PDOS C v5.4b under RT-11. ASxxxx has been built with Borland
  24022. C++ v3.1 under MS-DOS and includes a project (.PRJ) file. Although only these
  24023. implementations have been specifically tested, Baldwin claims many other K&R C
  24024. compilers may work as well.
  24025. ASxxxx Part 3 includes a comprehensive 80-page manual covering functionality
  24026. provided by all three existing ASxxxx cross assemblers and linkers. The
  24027. documentation lays out the exact specifications of syntax for symbols, labels,
  24028. assembler directives, and expressions in detail. The manual includes
  24029. appendices with instruction set highlights and supported syntax for Motorola
  24030. 6800, 6801, 6804, 6805, 68HC08, 6809, 6811 6816, Intel 8080 and 8085, and
  24031. Zilog Z80 and HD64180 CPUs.
  24032. The ASxxxx assembler falls short of full macro implementation, but does
  24033. include a host of important features such as: if/then/else, #include files,
  24034. radix support from binary to hexadecimal, and a full complement of C-language
  24035. operators for expressions. The ASxxxx linker goes beyond conventional loaders
  24036. by resolving intermodule symbols, combining code into segments, relocating
  24037. absolute symbols and base addresses, and producing either Intel HEX or
  24038. Motorola S19 output files.
  24039.  
  24040.  
  24041. MINED Editor: CUG #399
  24042.  
  24043.  
  24044. MINED, by Thomas Wolff (Freie Universität Berlin, Institut für Informatik,
  24045. Germany), is a modeless full-screen text editor. MINED was originally written
  24046. for MINIX and now works with most UNIX platforms as well as MS-DOS, and DEC
  24047. VAX-11/VMS. MINED works best at editing small files (50K or less) and can edit
  24048. many files simultaneously. Unlike other editors which have separate command
  24049. modes and input modes, MINED uses a modeless design for ease of use. It also
  24050. includes powerful regular expression operations for both searching and
  24051. replacing text. MINED Version 3 (released 08/04/93) is immediately available
  24052. as CUG volume #399.
  24053.  
  24054.  
  24055. Thompson-Davis Editor Update: CUG #386
  24056.  
  24057.  
  24058. The Thomson-Davis Editor, as provided by Frank Davis (Tifton, GA), is a
  24059. multi-file/multi-window binary and text file editor written for IBM PCs and
  24060. close compatibles. Thomson-Davis Editor (TDE) works well with batch files,
  24061. binary files, text files, and various computer language source code files. TDE
  24062. can handle any size of file and any number of windows that fit in conventional
  24063. DOS memory.
  24064. Davis reports the following enhancements since TDE was last released to the
  24065. CUG Library:
  24066. Pop-up pull-down command menu = <CTRL>+\
  24067. More Language support, thanks to Byrial Jensen, <byrial@daimi.aau.dk>
  24068. TDE ported to Linux (POSIX, SVR4, BSD4.3+?, FIPS 151-1, etc.)
  24069. A bug (a blunder, actually) got fixed in the 3.1 config utility.
  24070. Linux FAQs and HOWTOs
  24071. New regular expression meta characters: < = Empty string at beginning of word;
  24072. > = Empty string at end of word
  24073.  
  24074.  
  24075. Non-English Language Support
  24076.  
  24077.  
  24078. Byrial Jensen contributed several functions to TDE that are useful with
  24079. non-English languages. Using these functions, DOS filenames can contain
  24080. extended ASCII characters. As a result, the dirlist function in TDE (which
  24081. sorts filenames according to the sort order array) can be customized to your
  24082. favorite alphabet. Byrial also contributed two new macro functions that look
  24083. at the Caps Lock key: IfCapsLock and IfNotCapLock. Other changes supporting
  24084. non-English usage are as follows: predefined regular expression macros may be
  24085. redefined; all editor prompts have been gathered into prompts.h; response
  24086. letters have been gathered into letters.h; and the window letters can be
  24087. changed to follow a non-English alphabet.
  24088.  
  24089.  
  24090. Improved Regular Expression Handling
  24091.  
  24092.  
  24093. Davis writes: "I use the regular expression search much more often than I
  24094. first anticipated. A couple of features missing in the original implementation
  24095. are the beginning-of-word and end-of-word metacharacters. These metacharacters
  24096. really come in handy for culling prefixes and suffixes from the search. Here's
  24097. our new regular expression table: [Please refer to Table 1]"
  24098. TDE version 3.2a (Released 11/15/93) immediately replaces version 3.0 and is
  24099. available as CUG Library volume #386.
  24100. Table 1 Regular Expession operator precedence: (based on the table in Dr.
  24101. Aho's book)
  24102.  c = char x = string r,s = regular expression
  24103. -------------------------------------------------------
  24104. c any non-operator character Felis
  24105. \c c literally and C escapes catus\.
  24106. \:c predefined macro: \:c*(ieei)
  24107.  \:a - alphanumeric
  24108.  \:b - white space
  24109.  \:c - alphabetic
  24110.  \:d - decimal
  24111.  \:h - hex
  24112.  \:l - lower alpha
  24113.  \:u - upper alpha
  24114. . any character but newline c.t
  24115. < beginning of word <cat
  24116. > end of word <cat>
  24117. ^ beginning of line ^cat>
  24118. $ end of line cat$
  24119. [x] any character in x [a-z0-9]
  24120. [^x] any character not in x [^AEIOU]
  24121. r* zero or more r's ca*t
  24122.  
  24123. r+ one or more r's ca[b-t]+
  24124. r? zero or one r c.?t
  24125. rs r followed by s ^$
  24126. rs either r or s kitty½cat
  24127. (r) r (c)?(a+)t
  24128.  
  24129.  
  24130.  
  24131.  
  24132.  
  24133.  
  24134.  
  24135.  
  24136.  
  24137.  
  24138.  
  24139.  
  24140.  
  24141.  
  24142.  
  24143.  
  24144.  
  24145.  
  24146.  
  24147.  
  24148.  
  24149.  
  24150.  
  24151.  
  24152.  
  24153.  
  24154.  
  24155.  
  24156.  
  24157.  
  24158.  
  24159.  
  24160.  
  24161.  
  24162.  
  24163.  
  24164.  
  24165.  
  24166.  
  24167.  
  24168.  
  24169.  
  24170.  
  24171.  
  24172.  
  24173.  
  24174.  
  24175.  
  24176.  
  24177.  
  24178.  
  24179.  
  24180.  
  24181.  
  24182.  
  24183.  
  24184.  
  24185.  
  24186. CUG Product Focus
  24187.  
  24188.  
  24189. C++ SIM
  24190.  
  24191.  
  24192.  
  24193.  
  24194. Victor R. Volkman
  24195.  
  24196.  
  24197. Victor R. Volkman received a BS in Computer Science from Michigan
  24198. Technological University. He has been a frequent contributor to The C Users
  24199. Journal since 1987. He is currently employed as Senior Analyst at H.C.I.A. of
  24200. Ann Arbor, Michigan. He can be reached by dial-in at the HAL 9000 BBS (313)
  24201. 663-4173 or by Usenet mail to sysop@hal95.com.
  24202.  
  24203.  
  24204.  
  24205.  
  24206. Introduction
  24207.  
  24208.  
  24209. This month's product focus is derived from documentation written by M.C.
  24210. Little and D.L. McCue. Little and McCue have provided this documentation,
  24211. which describes their C++ SIM simulation class library, expressly for reprint
  24212. in The C Users Journal.
  24213. C++ SIM is a class library which provides discrete process-based simulation
  24214. similar to that provided by SIMULA [Birtwhistle 73][Dahl 70] and has been used
  24215. in the work presented in [McCue 92]. Based on the facilities provided in
  24216. SIMULA, C++ SIM provides active objects (instances of C++ classes) as the
  24217. units of simulation using the type-inheritance facilities of C++ to convey the
  24218. notion of "activity."
  24219. C++ SIM is designed to be used with a user-supplied threads package. C++ SIM's
  24220. authors use Sun Microsystem's lightweight process (thread) package; however,
  24221. they have added this package to the simulation class hierarchy through an
  24222. abstract class definition so that other lightweight process packages can be
  24223. used instead with very little modification. Users of this framework can
  24224. replace existing classes as long as the replacements conform [Black 86] to the
  24225. original class definition.
  24226. This article describes the C++ SIM class hierarchy, and shows how it can be
  24227. used to further refine the simulation package [1].
  24228.  
  24229.  
  24230. The Class Hierarchy
  24231.  
  24232.  
  24233. Figure 2 illustrates the main class hierarchy within the simulation package.
  24234. The base class is Thread, which provides the minimum functionality required of
  24235. a threads library. Two classes, GNU-Thread and LWP_Thread, derive from the
  24236. Thread class to support the threads packages that were available to C++ SIM's
  24237. authors at the time of this writing. These classes are Sun's own lightweight
  24238. process package, and the GNU threads library. Class Thread_Type provides a
  24239. (relatively) transparent way to change from one thread implementation to
  24240. another. Class Process provides all operations required by the simulator to
  24241. control execution of all processes in the simulation. These classes are
  24242. described in the following sections.
  24243.  
  24244.  
  24245. The Threads Base Class
  24246.  
  24247.  
  24248. In keeping with the C++ programming model, classes obtain the thread
  24249. characteristic, necessary to convey the notion of "activity" within the
  24250. simulation environment, by inheriting an appropriate base class (in simulation
  24251. terms they become processes). In C++ SIM, all classes that provide the
  24252. abstraction of threads must be derived from the Thread base class. This base
  24253. class forces the derived class to provide a minimum set of operations required
  24254. for the management of threads. (The base class defines these operations as
  24255. pure virtual functions, and C++ requires that a deriving class define such
  24256. functions before an instance of the class can be declared.) These operations
  24257. are shown in the Thread class as follows:
  24258. class Thread
  24259. {
  24260. public:
  24261. virtual void Suspend() = 0; // pure virtual function
  24262. virtual void Resume() = 0;
  24263.  
  24264. virtual void Body() = 0;
  24265. virtual long Current_Thread() = 0;
  24266.  
  24267. virtual long Identity();
  24268. static Thread* Self();
  24269. };
  24270. When defined, the Suspend and Resume methods will give the thread package
  24271. specific ways of suspending and resuming execution of a thread respectively.
  24272. Body represents the controlling code for each object, i.e., the scope within
  24273. which the controlling thread will execute.
  24274. Current_Thread must be defined by the derived class, since it returns the
  24275. identity of the currently executing thread, which is specific to the thread
  24276. package used.
  24277. The base class itself implements the operations Identity and Self because some
  24278. threads packages do not provide similar functionality. Identity returns the
  24279. unique identity of the thread associated with a given object, and Self returns
  24280. the currently executing thread. Because Self is a static member function
  24281. programs can invoke it without creating an instance of the Thread class, i.e.,
  24282. programs may call Thread::Self().
  24283.  
  24284.  
  24285. The Class LWP_Thread
  24286.  
  24287.  
  24288. User classes which require separate threads of control using the Sun thread
  24289. package can be derived from the LWP_Thread class shown as follows:
  24290.  
  24291. class LWP_Thread : public Thread
  24292. {
  24293. public:
  24294. virtual void Suspend();
  24295. virtual void Resume();
  24296. virtual void Body() = 0;
  24297.  
  24298. virtual long Current_Thread();
  24299.  
  24300. thread_t Thread_ID();
  24301. static void Initialize();
  24302.  
  24303. protected:
  24304. static const int MaxPriority;
  24305. LWP_Thread(int priority = MaxPriority);
  24306. };
  24307. The MaxPriority constant represents the maximum priority at which a thread may
  24308. execute (by default all threads derived from this class execute at this
  24309. priority). Class LWP_Thread defines all of the pure virtual functions declared
  24310. in Thread except Body, which must be defined by the deriving class.
  24311. Initialize initializes the threads package prior to use. (Obviously the
  24312. operations performed within this method are thread package specific.)
  24313. Thread_ID returns more detailed (package-specific) information about the
  24314. associated thread.
  24315.  
  24316.  
  24317. The Process Class
  24318.  
  24319.  
  24320. Applications could derive from the Thread base class to provide active objects
  24321. in C++ outside of the simulation package. However, to become a process in the
  24322. simulation environment, a class must be derived from the Process base class.
  24323. This class is shown as follows:
  24324. class Process : public LWP_Thread
  24325. {
  24326. public:
  24327. virtual ~Process ();
  24328.  
  24329. static double CurrentTime ();
  24330.  
  24331. void ActivateBefore (Process&);
  24332. void ActivateAfter (Process&);
  24333. void ActivateAt (double AtTime = CurrentTime());
  24334. void ActivateDelay (double AtTime = CurrentTime());
  24335. void Activate();
  24336.  
  24337. void ReActivateBefore (Process&);
  24338. void ReActivateAfter (Process&);
  24339. void ReActivateAt (double AtTime = CurrentTime());
  24340. void ReActivateDelay (double AtTime = CurrentTime());
  24341. void ReActivate ();
  24342.  
  24343. void Cancel ();
  24344. double evtime ();
  24345. void set_evtime (double);
  24346.  
  24347. boolean idle ();
  24348. boolean terminated ();
  24349.  
  24350. virtual void Body () = 0;
  24351.  
  24352. protected:
  24353. Process ();
  24354.  
  24355. void Hold (double t);
  24356. void Passivate ();
  24357.  
  24358. };
  24359. idle returns either TRUE or FALSE depending upon whether the process is
  24360. currently in the simulation queue.
  24361. terminated returns either TRUE or FALSE depending upon whether the process is
  24362. terminated.
  24363. evtime returns the simulation time at which a process is due to be
  24364. reactivated; set_evtime enables a program to change this time.
  24365. The Hold method removes the active process from the head of the event queue
  24366. and schedules it to become active a specified number of time units later.
  24367. Passivate removes the currently active process from the event queue
  24368. altogether. If the process is to execute again the program must recreate it.
  24369. Cancel removes the process from the simulation queue or simply suspends it
  24370. indefinitely if it is currently not in the queue.
  24371. At any point in time, a process can be in one (and only one) of the following
  24372. states:
  24373. active: the process is at the head of the queue maintained by the scheduler
  24374. (to be described shortly) and its actions are being executed.
  24375. suspended: the process is in the queue maintained by the scheduler, scheduled
  24376. to become active at a specified time in the future.
  24377. passive: the process is not in the scheduler's queue. Unless another process
  24378. brings it back into the queue, it will not execute any further.
  24379. terminated: the process is not in the scheduler's queue and has no further
  24380. actions to execute.
  24381. There are five ways to activate a process, and similarly five ways to
  24382. reactivate a waiting process:
  24383. before another process (ActivateBefore and ReActivateBefore);
  24384. after another process (ActivateAfter and ReActivateAfter);
  24385. at a specified (simulated) time (ActivateAt and ReActivateAt);
  24386. after a specified (simulated) delay (ActivateDelay and ReActivateDelay);
  24387. activate now (at the current simulated time) (Activate and ReActivate).
  24388. (Note that if a process is already scheduled, reactivation will simply
  24389. re-schedule the process.)
  24390. The Current Time method returns the current simulation time; programs
  24391. typically call Curren time to control action relative to a given time period.
  24392.  
  24393.  
  24394. The Simulation Scheduler
  24395.  
  24396.  
  24397. As in SIMULA, simulation processes (entities) execute at their assigned
  24398. simulation time, which is typically determined by an appropriate distribution
  24399. function. Only one process executes in any instance of real time, but many
  24400. processes may execute at any instance of simulation time. Programs place
  24401. currently inactive processes in a simulation queue (the event queue), which is
  24402. arranged in order of increasing simulation time.
  24403. To coordinate the execution of these processes, the scheduler manages the
  24404. simulation queue as follows: when no process is currently active, the
  24405. scheduler selects a process to run from the head of the queue and (re-)
  24406. activates it. When no processes are left to execute, i.e., the queue is empty,
  24407. the simulation ends.
  24408. The simulation queue is organized as a tree to improve the efficiency of the
  24409. scheduling algorithm. All nodes (processes) at the same level of the tree are
  24410. assigned to the same simulation time, as shown in Figure 1.
  24411. Because the scheduler manages processes in the simulation environment it
  24412. cannot itself be a simulation process. Like the main thread to be described
  24413. later, the scheduler is a priority thread within the environment and as such
  24414. must be controlled in a slightly different manner than the other simulation
  24415. entities. The structure of the scheduler is extremely simple and is shown as
  24416. follows:
  24417. class Scheduler : public LWP_Thread
  24418. {
  24419. public:
  24420. Scheduler ();
  24421. ~Scheduler ();
  24422. void Body ();
  24423. double CurrentTime ();
  24424. };
  24425.  
  24426. Every simulation application must start one scheduler before the simulation
  24427. can begin. The example to be described near the end of this article
  24428. illustrates use of the scheduler.
  24429.  
  24430.  
  24431. Priority Threads
  24432.  
  24433.  
  24434. C++ SIM executes two "priority" threads which cannot be derived from the
  24435. Process base class and therefore must be activated and deactivated separately.
  24436. These threads are as follows:
  24437. the simulation scheduler: this thread must be activated via the Resume method
  24438. of the thread base class from which it is derived (e.g., LWP_Thread);
  24439. the thread associated with main. A program must suspend this thread to allow
  24440. other threads to run since this thread has the highest priority in the system.
  24441. Calling the Thread class Initialize method within the main body of the
  24442. simulation code adds this thread to the thread queue maintained by class
  24443. Thread. The thread's presence in the queue allows the Suspend method to act on
  24444. it when the program requirs it to become inactive (using the
  24445. Thread::Self()->Suspend() operation).
  24446.  
  24447.  
  24448. Distribution Functions
  24449.  
  24450.  
  24451. Simulations often require distribution functions of various events (e.g., the
  24452. rate of arrivals of jobs at a processor, or the time between failures for a
  24453. node). C++ SIM provides a set of classes which give access to various useful
  24454. distribution functions, including the following: RandomStream, UniformStream,
  24455. Draw, Exponentialstream, ErlangStream, HyperExponentialStream, and
  24456. NormalStream. By creating instances of these classes the simulation processes
  24457. can gain access to the appropriate distribution function. Figure 3 shows the
  24458. class hierarchy of the distribution functions.
  24459.  
  24460.  
  24461. RandomStream and NormalStream
  24462.  
  24463.  
  24464. Classes RandomStream and NormalStream illustrate how the distribution
  24465. functions are derived and show how further functions could be built.
  24466. RandomStream (from which all other distribution functions are derived) is
  24467. shown as follows:
  24468.  
  24469. class RandomStream
  24470. {
  24471. public:
  24472. RandomStream (long MGSeed = 772531L.
  24473. long LCGSeed = 1878892440L);
  24474. virtual double operator() () = 0;
  24475. double Error ();
  24476.  
  24477. protected:
  24478. double Uniform ();
  24479.  
  24480. private:
  24481. double MGen ();
  24482. double series[128];
  24483. long MSeed, LSeed;
  24484. };
  24485. The Error method returns a chi-square error measure on the uniform
  24486. distribution function. The Uniform method generates random numbers; Uniform
  24487. uses the linear congruential generator based on the algorithm from [Knuth
  24488. Vol2], and shuffles the results of the linear generator with the
  24489. multiplicative generator as suggested by [Knuth Vol2] [3] to obtain a
  24490. sufficiently uniform random distribution.
  24491. Class NormalStream is defined as follows:
  24492. class NormalStream : public RandomStream
  24493. {
  24494. public:
  24495. NormalStream (double Mean, double StandardDeviation);
  24496. virtual double operator() ();
  24497.  
  24498. private:
  24499. double Mean, StandardDeviation;
  24500. double z;
  24501. };
  24502. The operator() uses the polar method in [Knuth Vol2] [4] to implement the
  24503. NormalStream by making use of the Uniform method of RandomStream.
  24504.  
  24505.  
  24506. SIMSET
  24507.  
  24508.  
  24509. C++ SIM also provides entity and set manipulation facilities similar to those
  24510. provided by the SIMSET classes of SIMULA. These facilities break down into two
  24511. classes:
  24512. Link: the Link class provides elements of a doubly linked list;
  24513. Head: the Head class maintains doubly linked lists of Link elements.
  24514. Class link is defined as follows:
  24515. class Link
  24516. {
  24517. public:
  24518. virtual Ã¿Link ();
  24519.  
  24520. Link* Suc () const;
  24521. Link* Pred () const;
  24522.  
  24523. Link* Out ();
  24524. void InTo (head*);
  24525.  
  24526. void Precede (Link*);
  24527. void Precede (Head*);
  24528. void Follow (Link*);
  24529. void Follow (Head*);
  24530.  
  24531. protected:
  24532. Link ();
  24533. };
  24534. Because it makes no sense to create instances of Link objects, the constructor
  24535. for Link is protected -- programs must derive a class from Link to benefit
  24536. from its functionality.
  24537. Suc and Pred return the successor and predecessor of this list element
  24538. respectively. These functions return 0 if no such element exists.
  24539.  
  24540. Out removes the object to which it currently belongs (if any) from the linked
  24541. list. InTo makes this object the last element in a linked list if the list
  24542. exists; If the list doesn't exist Out attempts to remove the object from any
  24543. linked list to which it may belong.
  24544. Precede also places an object in a linked list. If Precede is passed another
  24545. Link element, say L, then if L is a member of a linked list, this object is
  24546. placed into the same linked list and immediately preceding L. If L isn't a
  24547. member of a linked list, the result is the same as for Out. If Precede is
  24548. passed a Head object it produces the same result as InTo.
  24549. Follow acts similarly to Precede, except that L.Follow(L1) inserts L
  24550. immediately after L1, and L.Follow(H), places L as the first element in H,
  24551. where H is a Head object.
  24552. Note that as in SIMULA, Link elements can only belong to one linked list at a
  24553. time.
  24554. Class Head is defined as follows:
  24555. class Head
  24556. {
  24557. public:
  24558. Head ();
  24559. virtual ~Head ();
  24560.  
  24561. Link* First () const;
  24562. Link* Last () const;
  24563.  
  24564. long Cardinal () const;
  24565. boolean Empty () const;
  24566.  
  24567. void Clear ();
  24568. }
  24569. First and Last return references to the first and last Link objects in the
  24570. list respectively. If the list is empty then these functions return 0.
  24571. Cardinal returns the number of Link objects in the list, and Empty returns
  24572. TRUE if the list is empty, FALSE otherwise. Clear removes all Link objects
  24573. from the list.
  24574.  
  24575.  
  24576. Example: Job Service Simulation
  24577.  
  24578.  
  24579. This example is taken from [Mitrani 82] and simulates a process scheduler for
  24580. a machine which attempts to execute as many process (jobs) as possible. The
  24581. machine can only process one job at a time and queues job requests until it
  24582. can deal with them. The machine is prone to failures, so started jobs will be
  24583. interrupted by such failures and delayed until the machine has been repaired
  24584. (reactivated), at which point it is forced to restart execution from the
  24585. beginning (i.e., it is placed at the head of the job queue). The main
  24586. processes within this example are:
  24587. Arrivals: this process controls the rate at which jobs arrive at the service
  24588. (Machine).
  24589. Breaks: this process controls the availability of the Machine by "killing" it
  24590. and restarting it at intervals drawn from a Uniform distribution.
  24591. Job: this process represents the jobs that the Machine must process.
  24592. Machine: this is the machine on which the service resides. Machine obtains
  24593. Jobs from the job queue for the service and then attempts to execute them. The
  24594. machine can fail and so the response time for Jobs is not guaranteed to be the
  24595. same every time the job is performed.
  24596.  
  24597.  
  24598. Arrivals
  24599.  
  24600.  
  24601. The Arrivals class definition is relatively simple since none of the other
  24602. processes invoke operations on it. Arrivals is defined as follows:
  24603. class Arrivals : public Process
  24604. {
  24605. public:
  24606. Arrivals (double);
  24607. ~Arrivals ();
  24608.  
  24609. void Body ();
  24610.  
  24611. private:
  24612. Exponential Stream* InterArrival Time;
  24613. };
  24614. The constructor initializes the stream from which the rate of Job arrivals is
  24615. drawn and the destructor simply cleans up before the object is destroyed:
  24616. Arrivals::Arrivals (double mean)
  24617. {
  24618. InterArrivalTime = new ExponentialStream(mean);
  24619. }
  24620. Arrivals::~Arrivals () { delete InterArrivalTime; }
  24621. The main body of Arrivals (shown below) simply waits for an amount of time
  24622. dictated by the rate of arrivals stream and then creates another Job. This
  24623. procedure is repeated until the simulation ends.
  24624. void Arrivals::Body ()
  24625. {
  24626. for (;;) // inifinite loop
  24627. {
  24628.  
  24629. Hold((*InterArrivalTime) ());
  24630. Job* work = new Job();
  24631. }
  24632. }
  24633.  
  24634.  
  24635. Job
  24636.  
  24637.  
  24638. Unlike Arrivals, which is an active entity within the simulation, the Job
  24639. class does not need to be a separate process, since it is simply enqueued when
  24640. it is created and dequeued by the Machine when it can be executed. All a given
  24641. Job must do is calculate how long it took to be "processed":
  24642. class Job
  24643. {
  24644. public:
  24645. Job ();
  24646. ~Job ();
  24647.  
  24648. private:
  24649. double ArrivalTime;
  24650. double ResponseTime;
  24651. };
  24652. Because no operations are invoked on instances of the Job class, its
  24653. constructor and destructor perform all of its work:
  24654. Job::Job ()
  24655. {
  24656. boolean empty;
  24657.  
  24658. ResponseTime = 0;
  24659. ArrivalTime = sc->CurrentTime();
  24660. empty = JobQ.IsEmpty();
  24661. JobQ.Enqueue(this); // place this Job on to the queue
  24662. Total Jobs++;
  24663.  
  24664. if (empty && !M->Processing() && M->IsOperational())
  24665. M->Activate(); // Machine idle as no Jobs in queue
  24666. // and not broken
  24667. }
  24668.  
  24669. Job::~Job ()
  24670. {
  24671. ResponseTime = sc->CurrentTime() - ArrivalTime;
  24672. TotalResponseTime += ResponseTime;
  24673. }
  24674.  
  24675.  
  24676. Queue
  24677.  
  24678.  
  24679. The program places jobs which are not being serviced in a job queue. As with
  24680. the Job class, an instance of Queue is not required to be active, and as such
  24681. Queue is not derived from the Process class.
  24682. Queue is defined as follows:
  24683. class Queue
  24684. {
  24685. public:
  24686. Queue ();
  24687. ~Queue ();
  24688.  
  24689. boolean IsEmpty ();
  24690. // returns TRUE if no Jobs in queue
  24691. long QueueSize ();
  24692. // returns number of Jobs in queue
  24693. Job* DeQueue ();
  24694.  
  24695. // returns head of queue
  24696. void Enqueue (Job*);
  24697. // places Job at tail of queue
  24698. };
  24699.  
  24700.  
  24701. Machine
  24702.  
  24703.  
  24704. The Machine process obtains Jobs from the queue and processes them. Since
  24705. Machine is prone to failures Jobs can take extended periods of time to
  24706. complete. Other simulation processes invoke various operations on the machine
  24707. (for example to determine whether or not it has failed):
  24708. class Machine : public Process
  24709. {
  24710. public:
  24711. Machine (double);
  24712. ~Machine ();
  24713.  
  24714. void Body ();
  24715.  
  24716. void Broken ();
  24717. void Fixed ();
  24718. boolean IsOperational ();
  24719. boolean Processing ();
  24720. double ServiceTime ();
  24721.  
  24722. private:
  24723. ExponentialStream* STime;
  24724. boolean operational;
  24725. boolean working;
  24726. };
  24727. As with the Breaks and Arrivals processes, Machine's constructor and
  24728. destructor initialize and delete the stream that dictates the time required to
  24729. process a Job.
  24730. Processing returns the current status of the machine, i.e., whether or not it
  24731. is executing a job:
  24732. boolean Machine::Processing () { return working; }
  24733. Broken and Fixed de-activate (crash) and re-activate the machine respectively:
  24734. void Machine::Broken () { operational = false; }
  24735. void Machine::Fixed () { operational = true; }
  24736. IsOperational indicates whether or not the machine is currently active (i.e.,
  24737. whether it has "crashed"):
  24738. boolean Machine::IsOperational () { return operational; }
  24739. ServiceTime returns the time required to service a given job based on the
  24740. relevant distribution function initialized within the constructor:
  24741. double Machine::ServiceTime () { return (*STime)(); }
  24742. The main body of the Machine gets a Job from the job queue (if one is
  24743. available) and attempts to process it before looping again:
  24744. void Machine::Body ()
  24745. {
  24746. for(;;)
  24747. {
  24748. working = true;
  24749.  
  24750. while (!JobQ.IsEmpty())
  24751. // continue as long as Jobs are available
  24752. {
  24753. Hold(ServiceTime());
  24754. Job* J = JobQ.Dequeue();
  24755.  
  24756. ProcessedJobs++;
  24757. // keep track of number of completed Jobs
  24758. delete J; // remove finished Job
  24759. }
  24760.  
  24761. working = false;
  24762. // no Jobs in queue so become idle
  24763.  
  24764. Cancel();
  24765. }
  24766. }
  24767.  
  24768.  
  24769. Breaks
  24770.  
  24771.  
  24772. The Breaks class defines a process which simply waits for a specific period of
  24773. time before "killing" the Machine process. This process then waits again
  24774. before re-activating the machine. The Breaks class definition is relatively
  24775. simple:
  24776. class Breaks : public Process
  24777. {
  24778. public:
  24779. Breaks ();
  24780. ~Breaks ();
  24781.  
  24782. void Body ();
  24783.  
  24784. private:
  24785. UniformStream* RepairTime;
  24786. UniformStream* OperativeTime;
  24787. boolean interrupted_service;
  24788. };
  24789. The constructor and destructor simply initialize and delete the streams used
  24790. by the Breaks process respectively.
  24791. The main body of the Breaks process activates and deactivates the Machine
  24792. process. The Machine fails and recovers according to the OperativeTime and
  24793. RepairTime distribution functions respectively. The body is defined as
  24794. follows:
  24795. extern Machine* M; // This is the machine used to
  24796. // service requests
  24797. extern Queue JobQ; // This is the queue from which
  24798. // Jobs are drawn
  24799.  
  24800. void Breaks::Body ()
  24801. {
  24802. for(;;)
  24803. {
  24804. Hold((*OperativeTime)());
  24805. M->Broken();
  24806. // de-activate the Machine
  24807. M->Cancel();
  24808. // remove Machine from Scheduler queue
  24809.  
  24810. if (!JobQ.IsEmpty())
  24811. interrupted_service = true;
  24812.  
  24813. Hold((*RepairTime)());
  24814. M->Fixed(); // re-activate the Machine
  24815. if (interrupted_service)
  24816. {
  24817. interrupted_service = false;
  24818. M->ActivateAt(M->ServiceTime() +
  24819. CurrentTime());
  24820. }
  24821. else
  24822. M->ActivateAt();
  24823. }
  24824. }
  24825.  
  24826.  
  24827. MachineShop
  24828.  
  24829.  
  24830.  
  24831. The MachineShop class is the core of the simulation; it starts up all of the
  24832. main processes involved, and when the simulation ends it prints out the
  24833. results.
  24834. class MachineShop : public Process
  24835. {
  24836. public:
  24837. MachineShop ();
  24838. ~MachineShop ();
  24839.  
  24840. void Body ();
  24841. void Await ();
  24842. };
  24843. The Body method starts up the other processes, such as the Machine, and then
  24844. waits until the number of processed Jobs is at least 100,000:
  24845. void MachineShop::Body ()
  24846. {
  24847. sc= new Scheduler(); // create the simulation
  24848. // queue scheduler
  24849. Arrivals* A = new Arrivals(10);
  24850. M = new Machine(8);
  24851. Job* J = new Job;
  24852. Breaks* B = new Breaks;
  24853.  
  24854. // activate the relevant simulation processes
  24855.  
  24856. B->Activate();
  24857. A->Activate();
  24858. sc->Resume(); // start up the scheduler
  24859. // - it is not a process
  24860.  
  24861. while (ProcessedJobs < 100000)
  24862. Hold(10000);
  24863.  
  24864. cout << "Total number of jobs processed"
  24865. << TotalJobs << endl;
  24866. cout << "Total response time" << TotalResponseTime << endl;
  24867. cout << "Avge response" <<
  24868. (TotalResponseTime/ProcessedJobs) << endl;
  24869. cout << "Avge number jobs present"
  24870. << (JobsInQueue/CheckFreq) << endl;
  24871.  
  24872. // end simulation by suspending processes
  24873.  
  24874. sc->Suspend();
  24875. A->Suspend();
  24876. B->Suspend();
  24877. }
  24878. It isn't necessary to explicitly activate the Machine process because the
  24879. Breaks or Jobs process will do this.
  24880. The Await method suspends the thread associated with main, thus allowing the
  24881. other simulation threads to execute:
  24882. void MachineShop::Await()
  24883. {
  24884. Resume();
  24885. Thread::Self()->Suspend();
  24886. }
  24887.  
  24888.  
  24889. Main
  24890.  
  24891.  
  24892. The main part of the simulation code initializes the various thread-specific
  24893. variables used (e.g., the maximum priority of a thread), creates the main body
  24894. of the simulation code (in this case MachineShop) and then suspends the thread
  24895. associated with main:
  24896. void main ()
  24897. {
  24898.  
  24899. LWP_Thread::Initialize();
  24900.  
  24901. MachineShop m;
  24902. m. Await(); // Suspend main's thread
  24903. // (NOTE: this MUST be done
  24904. // by all applications).
  24905. }
  24906.  
  24907.  
  24908. Conclusions
  24909.  
  24910.  
  24911. The authors of C++ SIM have endeavoured to provide a simulation package which
  24912. provides similar functionality to that of SIMULA, since SIMULA has fulfilled
  24913. the needs of users over many years. From their experiences of using SIMULA,
  24914. both as a general programming language and as a simulation tool, they believe
  24915. they have been successful. As a result of using C++ they also believe that
  24916. they have produced a simulation package having several advantages over SIMULA,
  24917. for example:
  24918. performance -- C++ compilers typically generate code that is several times
  24919. more efficient than similar SIMULA code, and as a result, simulations execute
  24920. correspondingly faster;
  24921. C++ provides more extensive object-oriented features than SIMULA, allowing,
  24922. for example, class instance variables to be either publicly or only privately
  24923. available. In SIMULA, every thing is public, affecting the way code is written
  24924. and providing extra problems during debugging.
  24925. C++ SIM incorporates inheritance throughout its design to an even a greater
  24926. extent than is already provided in SIMULA. For example, C++ SIM's I/O
  24927. facilities, random number generators, and probability distribution functions
  24928. are entirely object-oriented, relying on inheritance to specialize their
  24929. behavior. Hence, users can add new functionality (e.g., new random number
  24930. generators) with little effect on the overall system structure.
  24931.  
  24932.  
  24933. Acknowledgements
  24934.  
  24935.  
  24936. The authors would like to thank Professor Isi Mitrani for the help he has
  24937. given them in the development of this simulation package and the time he has
  24938. devoted to listening to their thoughts and problems. They would also like to
  24939. thank Ron Kerr for his help with the SIMULA language, and Dr. Graham
  24940. Partington for his comments on drafts of this article. The work reported here
  24941. has been supported by SERC/MOD Grant GR/H81078 and Esprit Broadcast (Basic
  24942. Research Project Number 6360).
  24943. References
  24944. [Birtwhistle 73] G. M. Birtwhistle, O-J. Dahl, B. Myhrhaug, K. Nygaard, Simula
  24945. Begin, Academic Press, 1973.
  24946. [Black 86] A. Black et al, "Object Structure in the Emerald System",
  24947. Proceedings of the ACM Conference on Object-Oriented Programming Systems,
  24948. Languages, and Applications, October 1986.
  24949. [Dahl 70] O-J. Dahl, B. Myhrhaug, K. Nygaard, "SIMULA Common Base Language,"
  24950. Technical Report S-22, Norwegian Computing Centre, 1970.
  24951. [Knuth Vol2] Knuth Vol2, Seminumerical Algorithms, Addison-Wesley: p. 117.
  24952. [McCue 92] D. L. McCue and M. C. Little, "Computing Replica Placement in
  24953. Distributed Systems," Proceedings of the 2nd Workshop on the Management of
  24954. Replicated Data, November 1992: pp. 58-61.
  24955. [Mitrani 82] I. Mitrani, Simulation Techniques for Discrete Event Systems,
  24956. Cambridge University Press, Cambridge, 1982: p. 22.
  24957. [Sedgewick 83] R. Sedgewick, Algorithms, Addison-Wesley, Reading MA, 1983: pp.
  24958. 36-38.
  24959. [Stroustrup 86] B. Stroustrup, The C++ Programming Language, Addison Wesley:
  24960. 1986.
  24961. Footnotes
  24962. [1] The software to be described in this paper is available via anonymous ftp
  24963. from arjuna. ncl. ac. uk
  24964. [2] The authors would like to thank Professor I. Mitrani for his help in
  24965. developing the multiplicative generator used in the simulation. It is based on
  24966. the following algorithm: Y[i+1] = Y[i] * 5^5 mod 2^26, where the period is
  24967. 2^24, and the initial seed must be odd.
  24968. [3] As suggested by Maclaren and Marsaglia.
  24969. [4] Due to Box, Mullers and Marsaglia
  24970. Figure 1 Head of simulation queue
  24971. Figure 2 Simulation class hierarchy
  24972. Figure 3 Class hierarchy for distribution functions
  24973.  
  24974.  
  24975.  
  24976.  
  24977.  
  24978.  
  24979.  
  24980.  
  24981.  
  24982.  
  24983.  
  24984.  
  24985.  
  24986.  
  24987.  
  24988.  
  24989.  
  24990.  
  24991.  
  24992.  
  24993.  
  24994.  
  24995.  
  24996.  
  24997. Editor's Forum
  24998. Well, I made it past my 50th birthday. Being a 50-year-old computer programmer
  24999. is definitely better than being 20 -- you get more toys to play with and more
  25000. autonomy in choosing what to do with them. But I confess that it's not as nice
  25001. as being 30, or even 40 -- I miss the energy that came with those relatively
  25002. youthful milestones. My main consolation is that I still have some energy
  25003. left, and I'm much wiser about how I spend it now than in years gone by.
  25004. I couldn't help but notice at 30 that many of my 40-year-old colleagues
  25005. weren't as active in the trenches as I was. Ten years later, I found out why.
  25006. With maturity in one's profession comes increasing demands to serve as
  25007. caretaker rather than front-line contributor. Someone has to write all those
  25008. proposals, job descriptions, requirements documents, requisitions, etc. They
  25009. eat time like candy and sure don't resemble programming. But only the most
  25010. dedicated techies can resist the siren lure of responsibility. The rest of us
  25011. get suckered into acting like grownups.
  25012. I couldn't help but notice at 40 that many of my 50-year-old colleagues
  25013. weren't even fighting interesting battles, or so it appeared to me at the
  25014. time. They seemed to attend interminable meetings and talk about policy
  25015. matters and other ephemeral abstractions. I mean, how important can it be to
  25016. draft international standards, or procedures for software quality control, for
  25017. heaven's sake? Now, after a decade or more of doing that sort of stuff, I've
  25018. come to see the merit in it. I've learned how to play the guru, or the
  25019. statesman, or the doddering old fool, as the need arises. I can even sit
  25020. through a two-hour meeting without squirming (excessively).
  25021. I still write the odd bit of code, or the odd requirements spec, and it's more
  25022. fun than ever. I spent the half-week before my birthday in New Jersey, beating
  25023. on the draft C++ standard with Andy Koenig, Tom Plum, Bjarne Stroustrup, and
  25024. others -- and I have to admit it was mostly enjoyable. The computer business
  25025. has never been more exciting than it is today. I have much to be grateful for.
  25026. When I stumbled into this field at the age of 19, I never dreamed it would
  25027. consume my entire adult career. Or that it would bring me so many rewards. I
  25028. can only hope that most of you who read this magazine can enjoy a comparable
  25029. passion. May your candle burn equally bright.
  25030. So how does it feel to be half a century old? My favorite quote on that topic
  25031. is from Lowell Thomas, who was asked on his birthday how it felt to be 80. He
  25032. said, "It's not bad, considering the alternative."
  25033. P.J. Plauger
  25034. pjp@plauger.com
  25035.  
  25036.  
  25037.  
  25038.  
  25039.  
  25040.  
  25041.  
  25042.  
  25043.  
  25044.  
  25045.  
  25046.  
  25047.  
  25048.  
  25049.  
  25050.  
  25051.  
  25052.  
  25053.  
  25054.  
  25055.  
  25056.  
  25057.  
  25058.  
  25059.  
  25060.  
  25061.  
  25062.  
  25063.  
  25064.  
  25065.  
  25066.  
  25067.  
  25068.  
  25069.  
  25070.  
  25071.  
  25072.  
  25073.  
  25074.  
  25075.  
  25076.  
  25077.  
  25078.  
  25079.  
  25080.  
  25081.  
  25082.  
  25083.  
  25084.  
  25085.  
  25086.  
  25087. New Products
  25088.  
  25089.  
  25090. Industry-Related News & Announcements
  25091.  
  25092.  
  25093.  
  25094.  
  25095. Blue Sky Releases Code Generator for OWL v2.0
  25096.  
  25097.  
  25098. Blue Sky Software Corporation has released WindowsMAKER Professional v5, a
  25099. Prototyper and C/C++ code generator for Windows and Windows NT, which lets
  25100. Borland C++ v4.0 users generate OWL v2.0 code. WindowsMAKER v5 integrates into
  25101. the Borland IDE and offers 50 features in addition to those offered by
  25102. AppExpert.
  25103. Features of WindowsMAKER v5 include: Visual Basic-like code editing, toolbar,
  25104. and context-sensitive SmartMenus; a Visual Screen Designer, which includes
  25105. drag-and-drop editing and a configurable tool palette; and a Preview Mode
  25106. which lets users preview and test run an application before compiling.
  25107. WindowsMAKER v5 also includes Switch-It Code Generation Modules (SIMs), which
  25108. let users switch to any language or major C++ library during the development
  25109. of an application. Users can choose to automatically generate ANSI C, MFC and
  25110. OWL from one design. Using WindowsMAKER v5, users can start a project and
  25111. decide the type of code to use later and target Windows, Win32s, and Windows
  25112. NT.
  25113. Other features of WindowsMAKER v5 include: toolbar support; application
  25114. templates; code generation for online help; special effect support for fonts,
  25115. colors, 3D buttons, patterns, and 256-color bitmaps; style and property
  25116. setting support for controls; MDI application support; and Extended
  25117. Functionality Modules (EFMs).
  25118. WindowsMAKER Professional v5 includes the ANSI C SIM and is $995. Registered
  25119. users of WindowsMAKER Professional v4 can upgrade for $269. For more
  25120. information contact Blue Sky Software Corporation, 7486 La Jolla Blvd., Suite
  25121. 3, La Jolla, CA 92037, (800) 677-4946 or (619) 459-6365; FAX: (619) 459-6366.
  25122.  
  25123.  
  25124. National Information Systems Releases ACCENT STP
  25125.  
  25126.  
  25127. National Information Systems, Inc. has released ACCENT STP (Sun Transition
  25128. Pack) v2.0, a source code translator for Sun developers who plan to migrate
  25129. their OPEN LOOK applications to Motif and the Common Desktop Environment.
  25130. ACCENT STP supports Motif v1.2 and X11R5 on Sun OS v4.1.x, and Solaris v2.x.
  25131. According to NIS, ACCENT STP will translate 80% to 100% of the C/C++
  25132. application source code produced by XView, OLIT, or Devguide GIL files,
  25133. including header files, where there are equivalent resources offered in Motif.
  25134. The translated output will be recognizable Motif C or C++ source code.
  25135. Features of ACCENT STP v2.0 include support for "drag and drop," TTY widgets,
  25136. and internationalization support. Other features of ACCENT STP include:
  25137. compliance to the Motif standard, compatibility with Solaris v2.4/COSE CDE,
  25138. and application portability to multiple hardware platforms.
  25139. ACCENT STP consists of four optional modules. The Devguide Conversion, XView
  25140. Conversion, and OLIT Conversion modules are $4,995 each. The WindowMaker GUI
  25141. Editor is $1,495. Motif consulting and training are also available. ACCENT
  25142. ToolKit, a Motif library and OPEN LOOK ToolKit which helps support OPEN LOOK
  25143. users after the source code is translated, is available for $2,495 when
  25144. purchased with ACCENT STP. For more information contact National Information
  25145. Systems, Inc., 4040 Moorpark Ave., Suite 200, San Jose, CA 95117, (800)
  25146. 441-5758; FAX: (408) 246-3127; e-mail: info@nis.com.
  25147.  
  25148.  
  25149. Select Software Upgrades C++ Designer
  25150.  
  25151.  
  25152. Select Software Tools has upgraded C++ Designer, an MS-Windows-based object
  25153. oriented analysis and design tool, which includes Microsoft's MFC2 Library and
  25154. the Borland Owl Library. Based on Rumbaugh's Object Modeling Technique (OMT),
  25155. C++ Designer lets the user structure systems graphically, adding classes and
  25156. relationships using the GUI. The most recent release allows users to select
  25157. from classes available in the Microsoft library or from their own class
  25158. library. Header files for C++ can be generated from the design and linked into
  25159. the Visual C++ environment.
  25160. Features of C++ Designer include: Windows CUA compliance; MDI interface; a
  25161. data dictionary browsing capability; access control via user-ID; export
  25162. facility to clipboard or to Windows metafile; project administration including
  25163. versioning, backup, restore, and off-line; and multi-page printing. C++
  25164. Designer also supports the object-oriented and C++ features including: single
  25165. and multiple-class inheritance; class properties such as attributes and
  25166. operations; associations including multiplicity, roles, qualifiers, and
  25167. specialized forms such as aggregations; defining the access, virtual and
  25168. static nature of attributes and operations, and the access and virtual nature
  25169. of base classes; and storage of additional descriptive and constraint
  25170. information for classes, attributes, operations, and associations.
  25171. C++ Designer can be integrated with Borland's C++ and Turbo C++, and
  25172. Microsoft's Visual C++. It can also be configured to integrate with other
  25173. Windows IDEs. Code frames can also be generated from C++ Designer. Code is
  25174. compatible with most C++ compilers including Borland, Microsoft, and Clarion
  25175. TopSpeed.
  25176. C++ Designer is $295 per user, including free technical support via a toll
  25177. free number. Upgrades are sold separately. Users who have purchased C++
  25178. Designer within the past three months will receive the MFC2 library at no
  25179. cost. For more information contact Select Software Tools, Ltd., 1526
  25180. Brookhollow Dr., #84, Santa Ana, CA 92705, (714) 957-6633; FAX: (714)
  25181. 957-6219.
  25182.  
  25183.  
  25184. Inmark Releases zApp Interface Pack
  25185.  
  25186.  
  25187. Inmark Development Corporation has released the zApp Interface Pack, an add-on
  25188. product to its C++ class libraries. zApp is a portable C++ Application
  25189. Framework that gives application developers cross-platform portability through
  25190. object-oriented C++ classes. zApp v2.0 is shipping on Windows, Windows NT, DOS
  25191. Test, DOS Graphics, and OS/2.
  25192. The zApp Interface Pack augments zApp by providing a set of additional classes
  25193. that add graphical elements to applications. Classes in the zApp Interface
  25194. Pack (ZIP) provide applications with toolbars and status lines, 3D custom
  25195. controls, bitmap buttons, and a table object.
  25196. Quoting Howard Love, President and CEO of Inmark, "Developers can now
  25197. incorporate the most advanced features of modern applications with just a few
  25198. lines of code. Toolbars, spreadsheet-like tables, and other high-level
  25199. controls, which would normally require weeks or months of engineering time,
  25200. are now available to developers with just a few lines of code."
  25201. Demonstration software of zApp is available on the Inmark BBS. For more
  25202. information contact Inmark Development Corporation, 2065 Landings Dr.,
  25203. Mountain View, CA 94043, (415) 691-9000; FAX: (415) 691-9099; BBS: (415)
  25204. 691-9990, CompuServe GO INMARK
  25205.  
  25206.  
  25207. StratosWare Releases MemCheck for ANSI and K&R Platforms
  25208.  
  25209.  
  25210. StratosWare Corporation has released an ANSI-compliant source code version of
  25211. MemCheck, its error-detection tool for C/C++. MemCheck can be used for
  25212. development projects built with ANSI or K&R compliant C/C++ compilers.
  25213. MemCheck for ANSI and K&R can detect memory overwrites and underwrites, memory
  25214. leaks, heap corruption, and out-of-memory conditions, and requires no source
  25215. code changes for most projects. MemCheck integrates with existing C/C++ code
  25216. and works at run time to identify errors by source file and line number. The
  25217. error messages may be directed to the screen, written to log files, or sent in
  25218. network or e-mail messages.
  25219. MemCheck for ANSI and K&R includes source code and requires no debugging
  25220. information and no special compilation options. Developers may ship
  25221. applications with MemCheck linked in, royalty-free, allowing detection of
  25222. errors at beta or customer sites. According to StratosWare, applications with
  25223. MemCheck linked in run unchanged and at full speed. MemCheck may be switched
  25224. on or off at run time, linked out via a "Production" library, or compiled
  25225. completely out with no source code changes.
  25226. MemCheck for ANSI and K&R is $199. StratosWare offers free technical support
  25227. via fax, CompuServe, Internet, and a toll free number. For more information
  25228. contact StratosWare Corporation, 1756 Plymouth Rd., Suite 1500, Ann Arbor,
  25229. MI48105, (313) 996-2944; FAX: (313) 747-8519.
  25230.  
  25231.  
  25232. Quadralay Ship UDT for C/C++
  25233.  
  25234.  
  25235. Quadralay Corporation has begun shipping UDT for C/C++ v1.2, an open UNIX
  25236. development environment. UDT for C/C++ works with existing tools and code
  25237. base, and acts as an incremental front end to existing compilers and other
  25238. development tools. Features of UDT for C/C++ include: source code browsing,
  25239. editing, project management, compiling, and prototyping for C++ classes and
  25240. libraries.
  25241. UDT for C/C++ v 1.2 supports SPARC SunOS v4.1.x, SPARC Solaris v2.x, Intel SCO
  25242. Open Desktop, Intel Solaris v2.x, HP 9000/700, and the RS/6000. UDT for C/C++
  25243. v1.2 single and multiple licenses are $595 and $795 per seat respectively. A
  25244. free, thirty-day evaluation with online documentation is available via
  25245. anonymous ftp from ftp.quadralay.com (192.195.32.1) or by request. For more
  25246. information contact Quadralay Corporation, 8920 Business Park Dr., Austin TX
  25247. 78759, (512) 346-9199; FAX: (512) 794-9997; e-mail: info@quadralay.com.
  25248.  
  25249.  
  25250.  
  25251. Sector Seven Releases MakeMasterv2.6
  25252.  
  25253.  
  25254. Sector Seven has released MakeMaster v2.6. MakeMaster (formerly DEPGEN), reads
  25255. C source code and include files to create makefiles. Features of MakeMaster
  25256. v2.6 include: support for C++, an optimized file search algorithm, flat
  25257. dependency lists, relative directory references, and custom compiler command
  25258. lines.
  25259. MakeMaster v2.6 supports multiple disk and directory projects, cross-compilers
  25260. and linkers, libraries, and DLLs. MakeMaster v2.6 runs under DOS v3.3+, and
  25261. supports ANSI C and C++, Borland's Turbo Make, and Microsoft's NMake.
  25262. MakeMaster v2.6 is $49.95. For more information contact Sector Seven, P.O. Box
  25263. 11391, Burke, VA 22009, (703) 866-9477.
  25264.  
  25265.  
  25266. Stewart, Dufour and Gossage Distributes ProminareTM
  25267.  
  25268.  
  25269. Stewart, Dufour and Gossage Ltd. have begun distributing Pominare Inc.'s
  25270. ProminareTM in North America. ProminareTM combines a graphically oriented GUI
  25271. design and code generation tool with an integrated development environment and
  25272. a programming editor. The GUI development part of ProminareTM supports PM
  25273. controls including those for MMPM/2 and Pen-PM. ProminareTM supports C/C++
  25274. compilers and CommonView. The GUI tool also supports direct creation of
  25275. resource files as well as programming code.
  25276. According to the company, the integrated development environment of
  25277. ProminareTM eliminates most of the common mistakes encountered in trying to
  25278. build OS/2 applications. ProminareTM's graphical interface supports
  25279. compiler-independent specification of common options required for compiling
  25280. and linking programs. The IDE works with the programming editor to manage
  25281. compiler and linker-detected errors.
  25282. ProminareTM's programming editor is PM-based but performs, according to the
  25283. company, like a character-based editor. The editor is integrated with the IDE
  25284. and the GUI design tool and provides links to on-line help for the IBM
  25285. Toolkits. The single-user and network versions of ProminareTM are $895 each.
  25286. Additional workstation modules are $795. There are no run-time fees or
  25287. royalties. For more information contact Stewart, Dufour & Gossage Ltd.,
  25288. 210-1730 Courtwood Cr., Ottawa, Ontario, K2C 2B5, Canada, (613) 225-2121; FAX:
  25289. (613) 225-2624; BBS: (613) 225-2968.
  25290.  
  25291.  
  25292. DDC International A/S Introduces 1stOBJECT EXEC
  25293.  
  25294.  
  25295. DDC International A/S has introduced 1st OBJECT EXEC, a C++-based real-time
  25296. operating system aimed at industrial use. DDC-I regards C++ as a highly
  25297. suitable candidate for safely solving control problems, and expects its
  25298. run-time system to find uses from electronic measuring systems to life and
  25299. safety-critical appplications. According to DDC-I, their C++ run-time system
  25300. can facilitate rapid development of complex systems and advanced
  25301. communications with the switches, sensors, and motors used in new designs.
  25302. For more infomation contact DDC International A/S, Gi. Lundtoftevej 1B,
  25303. DK-2800 Lyngby, Denmark, +45 45 87 11 44; FAX: +45 45 87 22 17; Telex: 37704
  25304. ddci dk.
  25305.  
  25306.  
  25307. Omega Systems Ships VERSIONS
  25308.  
  25309.  
  25310. Omega Systems has begun shipping VERSIONS, a version control system for
  25311. Windows. VERSIONS provides version control support for programmers by managing
  25312. multiple project resources including source code, documentation, or other
  25313. files. VERSION supports varied file formats, including binary files, and
  25314. doesn't limit the number or types of files which can be maintained. VERSIONS
  25315. is network-compatible and supports multiple developers working on the same
  25316. project, allowing storage of both temporary and permanent versions of a file.
  25317. Eschewing the approach of storing incremental changes to files, VERSIONS
  25318. stores previous versions of a file in a "master project," a proprietary format
  25319. that labels, maintains, and tracks files for access to both the most current,
  25320. as well as any past version. VERSIONS also tracks changes to a project either
  25321. on a server or local workstation through the master project. Users check files
  25322. into and out of the master project as needed and VERSIONS automates the
  25323. process of determining which files need to be checked in or out. VERSIONS is
  25324. $99. One copy of VERSIONS is required for each workstation in a networked
  25325. installation. For more information contact Omega Systems, 5405 Alton Parkway,
  25326. Suite 5A494, Irvine, CA 92714, (800) 458-5467 or (714) 253-6700; FAX: (714)
  25327. 253-6712.
  25328.  
  25329.  
  25330. EMS Professional Shareware Upgrades C/C++ Utility Library
  25331.  
  25332.  
  25333. EMS Professional Shareware has upgraded its C/C++ Utility Library on CDROM.
  25334. The library has over 1000 public domain and shareware products for programmers
  25335. using C/C++, Microsoft C, and Turbo C. The products are compressed on 46 1.44
  25336. Mb diskettes or one CD-ROM. All products in the library, and 150 commercial
  25337. products, are described in an indexed database which accompanies the library.
  25338. When a programmer needs to locate a particular type of C/C++ product, he or
  25339. she can find it by vendor, name, type, release date, or free text search
  25340. across descriptions. The C/C++ Utility Library contains a variety of files,
  25341. including: Array, Binary Tree, Communication, Compression, Database, Debugger,
  25342. Editor, Graphics, Linked List, Memory Management, MS Windows, MS Windows NT,
  25343. Paradox Engine, Program Generator, Reference, Spreadsheet, String, TSR/ISR,
  25344. Virus, and other types.
  25345. The C/C++ Utility library is $59.50 on CD-ROM or $149 for the diskette
  25346. versions. A subset of the C/C++ library containing 406 files for C++ only is
  25347. available on 20 diskettes for $59.50. All products come with a 30-day,
  25348. money-back guarantee. For more information contact EMS Professional Shareware,
  25349. 4505 Buckhurst Ct., Olney, MD 20832, (301) 924-3594; FAX: (301) 963-2708;
  25350. e-mail: eengelmann@worldbank.org.
  25351.  
  25352.  
  25353. Network Dynamics Upgrades Internationalization Toolkit
  25354.  
  25355.  
  25356. Network Dynamics has released the Internationalization Toolkit v3.0. The
  25357. toolkit is used by programmers to simplify and expedite the
  25358. internationalization/localization of software. Formerly known as "The String
  25359. Externalization Tools," the toolkit has been expanded to include support for
  25360. Windows and Presentation Manager string resource files, string length
  25361. tracking, string file appending, an extraction "undo" utility, string
  25362. replacement, handling of static initialization, and a graphical user
  25363. interface.
  25364. Version 2.0 features like string extraction, multi-byte Kanji support,
  25365. software lifecycle support, string tagging, string caching, and string
  25366. re-insertion have been expanded in version 3.0, with the intent of providing
  25367. greater flexibility and ease of use. The user's manual has also been expanded.
  25368. A "white paper" on the subject of software internationalization is available
  25369. as an option. Sample programs are included to illustrate DOS code page
  25370. swapping and keyboard manipulation.
  25371. The Internationalization Toolkit is available in source code form for C/C++.
  25372. Plans call for release of Turbo Pascal and Quick Basic versions in 1994. The
  25373. Internationalization Toolkit supports DOS, OS/2, and UNIX platforms. The
  25374. Internationalization Toolkit is $249.95 for a royalty-free source code license
  25375. for up to 10 programmers. For more information contact Network Dynamics, (804)
  25376. 220-8771; FAX: (804) 220-5741.
  25377.  
  25378.  
  25379. Cadre Technologies Introduces Ensemble Viewer
  25380.  
  25381.  
  25382. Cadre Technologies Inc. has released Ensemble Viewer, an interactive 2-D and
  25383. 3-D graphical tool for visualizing C programs.
  25384. Ensemble Viewer supports browsing of program flow, data structure, and
  25385. physical file structures. Ensemble Viewer provides interactive views of key
  25386. program aspects by displaying program information and test results that are
  25387. stored in the Ensemble database. Ensemble Viewer lets the user interact with
  25388. the software design, code, and files by viewing a graphical representation of
  25389. the actual program structures. This interaction lets a user understand the
  25390. program or see the impact of program changes without having to read through
  25391. the source code. Ensemble Viewer is available for Sun SPARCstations and the
  25392. company plans called for a release on HP9000 and IBM RS/6000 during the first
  25393. quarter of 1994. Prices for Ensemble Viewer start at $2,400 depending on
  25394. configuration. For more information contact Cadre Technologies Inc., 222
  25395. Richmond St., Providence, RI 02903, (401) 351-5950; FAX: (401) 351-7380.
  25396.  
  25397.  
  25398. TerraLogics Upgrades Mapping Software Toolkit
  25399.  
  25400.  
  25401. TerraLogics, Inc. has upgraded TerraView, its geographic mapping software
  25402. development kit. TerraView v4.0, lets developers embed geographic maps
  25403. directly into the source code of Windows applications developed with
  25404. Microsoft's Visual C++ and C/C++ v7, Borland's C++ v3.1, Gupta's SQL Windows,
  25405. and Powersoft's PowerBuilder.
  25406. Features of TerraView v4.0 include: raster data support, which lets developers
  25407. superimpose TerraView data maps over aerial photographs or satellite images;
  25408. style sheets, which are used to customize map displays; dynamic renditioning,
  25409. which supports style changes for a single use of a map; and faster updating of
  25410. mobile symbols for real-time tracking and display applications. TerraView v4.0
  25411. lets map users access data from many database managers, including Gupta
  25412. SQLBase, dBASE, and Oracles, as well as Defense Mapping Agency's Digital Chart
  25413. of the World and the Census Bureau's Topologically Integrated Geographic
  25414. Encoding and Referencing (TIGER) data.
  25415. TerraView v4.0 is available for MS-DOS and Windows, Apple Macintosh, and UNIX
  25416. systems from SUN, HP, and IBM. For more information contact TerraLogics, Inc.,
  25417. 600 Suffolk St., Lowell, MA 01854, (508) 656-9900; FAX: (508) 656-9999.
  25418.  
  25419.  
  25420.  
  25421. PractiSys Releases STORC GOLD v2.0
  25422.  
  25423.  
  25424. PractiSys has released v2.0 of STORC GOLD, its Windows form conversion tool.
  25425. STORC GOLD v2.0 lets Microsoft Visual C++ developers directly import Visual
  25426. Basic .FRM files into C++ projects, reusing both form design and VBX controls.
  25427. STORC GOLD v2.0 is $45.93 per copy per single user. Developers can download a
  25428. fully-functional evaluation copy from CompuServe (GO WINSDK or GO MSBASIC), or
  25429. can obtain a copy from PractiSys for $3. For more information contact
  25430. PractiSys, 4767 Via Bensa, Agoura, CA 91301, (818) 706-8877.
  25431.  
  25432.  
  25433. Computer Applications Specialists Introduces ProMet
  25434.  
  25435.  
  25436. Computer Applications Specialists has introduced ProMet, a C/C++ metrics
  25437. utility. ProMet measures program size, number of comments and comment density,
  25438. McCabe's and other complexity measurers, level of nesting, number of program
  25439. jumps, and number of literals. These measures provide quantitative data for
  25440. estimation of effort and conformance to programming style guidelines.
  25441. In addition to calculation of metrics for each function and each module,
  25442. ProMet provides program summary data and a "Top Ten" report that highlights
  25443. the ten functions that have the highest metric scores. The Top Ten functions
  25444. are presented both in a file list and as a graph to highlight the code
  25445. sections that have the highest likelihood of having errors. The Top Ten list
  25446. lets testing and review resources focus on code areas that are the most
  25447. complex and may suggest the need for additional oversight.
  25448. ProMet is $99 and comes with a manual that provides a background on software
  25449. metrics and their use. For more information contact Computer Applications
  25450. Specialists, 9948 Hibert St., Suite 103, San Diego, CA 92131, (619) 695-2600;
  25451. FAX; (619) 695-0794.
  25452.  
  25453.  
  25454. Digital Information Systems Corporation Ports PVCS to Alpha AXP
  25455.  
  25456.  
  25457. Digital Information Systems Corporation and Digital Equipment Corporation have
  25458. ported Intersolv's PVCS Version Manager v5 and PVCS Configuration Builder v5
  25459. products to Digital's Alpha AXP. Under the terms of the agreement, DISC is
  25460. porting, distributing, and providing technical support for PVCS products on
  25461. operating systems not supported by Intersolv. DISC has completed ports to
  25462. OpenVMS, VAX/VMS, OSF/1, and several UNIX-based platforms.
  25463. Features of PVCS Version Manager v5 include: SQL support facilities, expanded
  25464. file support, user-defined delta generation, and internationalization
  25465. capabilities. Features of PVCS Configuration Builder v5 include: run-time
  25466. diagnostics, build techniques, directives, and build script compilation
  25467. capabilities.
  25468. The price for PVCS Version Manager v5 starts at $599 for a 1-4 user license
  25469. and Configuration Builder starts at $399. For more information contact Digital
  25470. Information Systems Corporation, 11070 White Rock Rd., Rancho Cordova, CA
  25471. 95670, (800) 366-3472 or (916) 635-7300; FAX: (916) 635-6549.
  25472.  
  25473.  
  25474. Odyssey Development Releases ISYS Search Engine
  25475.  
  25476.  
  25477. Odyssey Development, Inc., has released the ISYS Developers' Toolkit, which
  25478. includes Odyssey's ISYS text retrieval software. Developers and OEMs can
  25479. integrate the ISYS text retrieval engine into applications for CD-ROM
  25480. authoring, electronic publishing, or document and image management. ISYS can
  25481. access textual information stored in word processor and other files. ISYS can
  25482. read 28 word processor formats, as well as some spreadsheet and database
  25483. files. Documents to be searched remain in their native formats. The
  25484. Developers' Toolkit also lets OEMs develop External Access Modules. ISYS can
  25485. then access and index "foreign" data sources, such at text stored in a
  25486. relational database.
  25487. The ISYS engine is available for Microsoft Windows and MS-DOS. The ISYS engine
  25488. can be called from many major languages. Sample code is provided for C,
  25489. Pascal, and Visual Basic. For more information contact Odyssey Development,
  25490. Inc., 650 S. Cherry St., #220, Denver, CO 80222, (303) 394-0091: FAX: (303)
  25491. 394-0096.
  25492.  
  25493.  
  25494. SLR Systems Upgrades OPTLINK
  25495.  
  25496.  
  25497. SLR Systems Inc. has upgraded OPTLINK for Windows. OPTLINK v5.0 for Windows
  25498. lets developers generate compressed executables (.EXE) and Dynamic Link
  25499. Libraries (.DLL). According to the company, OPTLINK v5.0 for Windows can
  25500. reduce file size by 50 percent. Compressed EXEs and DDLs generated by OPTLINK
  25501. become self-loading and decompress automatically as segments are demanded.
  25502. OPTLINK v5.0 supports Borland, Microsoft, and Symantec C++ compilers.
  25503. Debugging support is included for Borland's Turbo Debugger and Microsoft's
  25504. CodeView. As a DOS-hosted linker, OPTLINK is distributed in two forms, real
  25505. mode and protected mode (DPMI).
  25506. OPTLINK v5.0 for Windows is $350. Registered owners of v4.0 will receive the
  25507. v5.0 update free, while other previous owners can purchase the update for
  25508. $165. For more information contact SLR Systems, Inc., 1622 N. Main St.,
  25509. Butler, PA 16001, (412) 282-0864; FAX: (412) 282-7965; BBS: (412) 282-2799.
  25510.  
  25511.  
  25512. Segue Releases QA Partner for NT and OS/2
  25513.  
  25514.  
  25515. Segue Software has released its QA Partner cross-platform GUI test tool for
  25516. IBM's OS/2 and Microsoft NT. Using QA Partner, developers can test
  25517. applications on either of these platforms, and the test scripts will be
  25518. portable across the other platforms QA Partner supports, including Windows,
  25519. Macintosh, VMS, and Motif.
  25520. QA Partner for both IBM OS/2 and Microsoft NT is $1,495. For more information
  25521. contact Segue Software, Inc. 1320 Centre St., Newton Centre, MA 02159, (617)
  25522. 969-3771 :FAX: (617) 969-4326.
  25523.  
  25524.  
  25525. Intel Ships Plug and Play Kits
  25526.  
  25527.  
  25528. Intel Corporation has begun shipping three Plug and Play Development kits. The
  25529. kits are designed to help developers in making PCs easier to configure. The
  25530. Plug and Play Kits contain the software to perform the automatic configuration
  25531. of new Plug and Play cards and Plug and Play-ready systems.
  25532. The first kit, the "Plug and Play Kit for MS-DOS and Windows," includes a DOS
  25533. driver, interface libraries, a configuration utility, a VHDL description for
  25534. an ASIC implementation, and reference manuals. The second kit, the "Plug and
  25535. Play BIOS Enhancements Kit," contains BIOS software that detects and
  25536. configures PCI and Plug and Play ISA add-in cards. Software is available in
  25537. source code form. The third kit, "The Plug and Play ISA Hardware Demo Kit,"
  25538. contains a functional audio Plug and Play ISA demo card and Windows v3.1
  25539. Virtual Device Drivers. The kit also includes diagnostics, board schematic
  25540. files, and speakers. Bundled with this kit is the "Plug and Play Kit for MS
  25541. DOS and Windows."
  25542. The Plug and Play ISA Hardware Demo Kit is $895. For more information contact
  25543. Intel Literature Packet #F8PO1, P.O. Box 7641, Mr. Prospect, IL 60056, (800)
  25544. 548-4725.
  25545.  
  25546.  
  25547.  
  25548.  
  25549.  
  25550.  
  25551.  
  25552.  
  25553.  
  25554.  
  25555.  
  25556.  
  25557.  
  25558.  
  25559.  
  25560.  
  25561.  
  25562.  
  25563.  
  25564.  
  25565.  
  25566.  
  25567.  
  25568.  
  25569.  
  25570.  
  25571.  
  25572.  
  25573.  
  25574.  
  25575.  
  25576.  
  25577.  
  25578.  
  25579.  
  25580.  
  25581.  
  25582.  
  25583.  
  25584.  
  25585.  
  25586.  
  25587.  
  25588.  
  25589.  
  25590.  
  25591.  
  25592.  
  25593.  
  25594.  
  25595.  
  25596.  
  25597.  
  25598.  
  25599.  
  25600.  
  25601.  
  25602.  
  25603.  
  25604.  
  25605.  
  25606.  
  25607.  
  25608.  
  25609.  
  25610.  
  25611.  
  25612.  
  25613.  
  25614.  
  25615. We Have Mail
  25616. Dear Mr. Plauger:
  25617. There are a number of aspects of the current state of the art of software
  25618. engineering that I find less than satisfactory. They are
  25619. 1) Bug-infested tools
  25620. 2) The dearth of productive engineering tools
  25621. 3) The programming languages themselves.
  25622. First of all, I have used Lattice C, Turbo C, and Microsoft and Borland C/C++
  25623. compilers. Our group chose the latter two for their third-party and Windows
  25624. support. First I used Lattice C 3.1. It didn't do floats but doubles seemed to
  25625. work OK. Next I used Microsoft v5.0. It optimized code out of existence.
  25626. Because of that we switched to Turbo C/C++. It refused to do assignments of
  25627. floating point (float or double) literals in the range between 0 and 1!
  25628. This problem only occurred when linking together 20-odd modules, In a
  25629. three-module test program all worked fine. Due to project deadline pressure I
  25630. was never able to whack away at the 20-odd modules to the point where the
  25631. problem went away in order to determine its cause. I worked around it. This
  25632. problem was also present in Borland C/C++ 3.0, but does not occur in version
  25633. 3.1 with identical source code. I have seen two instances where Borland C/C++
  25634. 3.1 handled the following snippet where the function returns the value 1 by
  25635. not taking the if:
  25636. if ( func() ) // returns 1
  25637. do_something(); // never executed
  25638. Changing this to the following worked, however:
  25639. status = func();
  25640. if ( status )
  25641. do_something()
  25642. My first gripe about the state of the art is that compiler vendors seem to
  25643. concentrate on features like IDEs, 32-bit versions (64-bit next, no doubt),
  25644. Windows support, etc., while basic functionality is not solid. Floating point
  25645. seems especially difficult for these people. (You'd think that after ten years
  25646. of writing C compilers...) I really don't know if Borland C/C++ v3.1 has
  25647. floating point problems; I just haven't seen any problems so far. Now if
  25648. Borland and Microsoft are the least-buggy products on the market, I'd hate to
  25649. see the others. I would love to be able to trust the compiler, but that seems
  25650. to be wishful thinking. At least we have debuggers to go roto-rooting in the
  25651. generated assembly code.
  25652. More and more vendors are charging for phone support. Now I get to pay them
  25653. for reporting bugs in their products. Borland actually told me to call their
  25654. 900 number to discuss a bug! No thanks! Along with the bug situation, I wish
  25655. compiler error and warning messages were more accurate. In many cases I have
  25656. to ask myself, "Now what does it really mean?" I use PC-LINT religiously. To
  25657. the compiler vendor's credit, error and warning messages have been getting
  25658. better in recent years.
  25659. Now lets add C++ on top of this whole mess. I enjoyed your article,
  25660. "Programming Language Guessing Games" in Dr. Dobb's (October 1993). I agree of
  25661. course with your suggestion to "pick a subset of the [C++] language that
  25662. minimizes surprises, learn it well, and don't stray from it." The problem is
  25663. that the compiler vendor chooses to support everything that looks like it will
  25664. make it through the ANSI committee. If they can't get some of these simple
  25665. things solid (like the function return value example above) how can I trust
  25666. them to do some of the complicated things you have described for C++? Even if
  25667. I do restrict myself to a subset of the language, it could be that the other
  25668. features induce bugs in the parts that I am using. The bottom line is that the
  25669. majority of our time is spent on maintenance. Compiler bugs cost us. Secondly,
  25670. the support tools available do not help me with software engineering (as
  25671. opposed to hacking which I define as, "code it first without thought of
  25672. design, document it later") as I would like. Diagrams are a neat idea, but
  25673. then we have to translate the diagram into code by hand and translation is
  25674. where errors creep in. Management might go for the time spent on diagramming
  25675. if automatic code generators were available. Code generators for Windows
  25676. screens and other user interfaces are generally available, but I haven't seen
  25677. any for other parts of a program. A framework generator would be nice.
  25678. Hardware engineers have schematic capture and printed-circuit-board layout
  25679. tools including auto routers, but as software engineers, we do analogous
  25680. things by hand. Also, I've yet to see a decent cross-reference generator after
  25681. evaluating several. The ones with usable output are way too slow. I use GNU
  25682. CTAGS, but wish it did more. I know of no general purpose, highly configurable
  25683. cross-reference generator. We have written our own programs and editor macros
  25684. to insert comment blocks for functions and modules and for extracting those
  25685. into a Word document, but so much more could be done. I am beginning to write
  25686. add-on tools for Codewright, a very extensible Windows programmer's editor.
  25687. Thirdly, despite some ugly features of C, I like it a lot (more than Pascal,
  25688. Modula-3, Eiffel, etc.). For example, C overloads the static and void
  25689. keywords, allows functions to return pointers to automatic variables, and
  25690. various other gotchas mentioned in Andrew Koenig's C Traps and Pitfalls.
  25691. (Being able to execute an array of opcodes is a feature?) But for the sadist,
  25692. C++ is a real treat. Now we have not two but three meanings of static and not
  25693. two but four meanings of void!
  25694. C and C++ violate many of the accepted guidelines of language design. Many
  25695. things can be done several different ways, much of it behind the scenes. I
  25696. shudder whenever I read one of your articles concerning C++, your latest in
  25697. The C User's Journal on the Standard C++ library included (P.J. Plauger,
  25698. "Standard C: Developing the Standard C++ Library," CUJ, October 1993). I know
  25699. that I cannot avoid learning C++, but hopefully it will be replaced soon with
  25700. something simpler. We need new paradigms and metaphors to handle advanced
  25701. concepts. Languages will have to become more complicated, but they should be
  25702. as simple as possible. I like the approach taken by Meyer in Eiffel of
  25703. including an extensive set of libraries. The less I have to write myself, the
  25704. more productive I am. Hopefully, libraries supplied with compilers are well
  25705. thought-out and bug-free. I wish Borland C/C++ came with the library support
  25706. that Eiffel or NextStep Objective-C does. A collection of third-party classes
  25707. or libraries does not have the consistency that a single-sourced library does.
  25708. I have only touched the surface of some current problems in the software
  25709. industry. What I would like to know is:
  25710. 1) Which in your experience are the least-buggy C/C++ compilers?
  25711. 2) Do you know of a diagrammer w/code generator? What other engineering tools
  25712. have you found to be helpful?
  25713. 3) Do you know of any cross-reference generator that outputs a list of
  25714. identifiers including the module name, line number, and function (if any) they
  25715. were found in, and that is fast?
  25716. 4) What do you think of Modula-3, Eiffel, and IBM's Object REXX? Thank you in
  25717. advance for your help. Thanks for your excellent articles. Please keep them
  25718. coming.
  25719. BRUCEDICKEY@VAX.MICRON.COM
  25720. Bruce Dickey
  25721. Micron Semiconductor, Inc.
  25722. 2805 E. Columbia Rd., MS 892
  25723. Boise, ID 83706
  25724. Whew! You raise a whole slew of issues. I agree with you almost across the
  25725. board, except that I am a bit more sympathetic to the plight of all those
  25726. compiler vendors out there scrabbling for market share in a turbulent
  25727. industry. I guess that comes from selling compilers myself for ten years.
  25728. Growing complexity seems always to offset gains in our ability to manage
  25729. software development and reduce shipped bugs.
  25730. I don't know how to answer any of your questions at all well, but I invite
  25731. other readers to chip in. My opinion of Modula-3 and Eiffel, for what it's
  25732. worth, is fairly simple. Both offer interesting combinations of features, but
  25733. neither has quite pulled off the synergy of C or C++. I don't know enough
  25734. about REXX to comment. -- pjp
  25735. Dear Editor,
  25736. My compiler issues an error diagnostic on the call to g in the code fragment
  25737. shown in Listing 1, but no error on the call to f The error claims that there
  25738. is a pointer mismatch in the argument. I am using Digital's DEC C compiler
  25739. under OpenVMS on their new Alpha processor. When I contacted DEC's customer
  25740. service, they explained that the ANSI standard allows conversion from a
  25741. pointer to a qualified type to a pointer to a non-qualified type, but does not
  25742. allow 'ignoring' the qualifier for a pointer to a pointer to a type. Their
  25743. argument seemed somewhat niggling to me, so I wanted to appeal to a higher
  25744. authority. How do you think a compiler should handle this code (shown in
  25745. Listing 1)? Sincerely,
  25746. Dick Hile
  25747. T and B Computing, Inc.
  25748. 24 Frank Lloyd Wright Dr.
  25749. P.O. Box 302 - Lobby A
  25750. Ann Arbor, Michigan 48106-0302
  25751. DEC is correct. The distinction may appear niggling to you, but we meant to do
  25752. it that way when we wrote the C Standard. -- pjp
  25753. Dear Mr. Plauger,
  25754. I would like to add my two cents' worth to Mr. Mike Musielski's comments (CUJ,
  25755. October 1993) on the nature of C as a language. ("Is C a language?" was his
  25756. opening question.) The short answer to that question is, no, C is not a
  25757. language. Neither is any programming language. The programming-as-writing
  25758. analogy is interesting, but like most analogies it is partly true and partly
  25759. dangerous.
  25760. A programming language is really a notation system: defined narrowly,
  25761. conceived as a formal means of expression, and limited in its range of
  25762. expressiveness, at least when compared with natural languages. No programming
  25763. language is used as an end in itself (except of course by participants in
  25764. Obfuscated C contests), whereas a natural language is used that way all the
  25765. time by artists whose only goal it is to explore the language's poetic
  25766. possibilities. This is the main reason why programs, no matter how short,
  25767. cannot be compared to short stories or other forms of fiction.
  25768. Nevertheless, there is a basis for the analogy. Like natural languages,
  25769. programming languages do evolve and adapt themselves to new situations and
  25770. conditions. And as they do, new vocabulary creeps in, functions begin to
  25771. overlap, and things begin to get -- well, messy. Even standards cannot clear
  25772. up all ambiguities: there will always be more than one way to skin a cat. You
  25773. can say "pass away" or "kick the bucket," just as you can use strtol or atol.
  25774. Both elements in each pair approximate, but do not exactly map, the same idea.
  25775. How are you to recognize when it is appropriate to use one and not the other?
  25776. This is what Mr. Musielski wants to know.
  25777. There are some useful texts out there. I suggest Mr. Musielski look at (again,
  25778. if not for the first time) The C Programming Language, by Kernighan and
  25779. Ritchie. Another book I have found useful on occasion is C Lab Notes, by
  25780. Flanders and Holmes. This book has a nice feature in that each chapter has at
  25781. its head a list of the tasks that are to be explored inside ("forward and
  25782. reverse scrolling," "sending a message to another node," etc.). And, not
  25783. least, The C Users Journal is itself very instructive in matters of language
  25784. use. None of these texts is consciously "literary," but all take the implicit
  25785. stance that a programming language is an idiom to be mastered. Mastering the
  25786. language (your "material") is the goal of literary writing as well.
  25787. I think what might be very beneficial to Mr. Musielski (and others, myself
  25788. included) would be a kind of reverse dictionary: a list of common tasks along
  25789. with the range of functions, procedures, tools, etc. that could be used to
  25790. fulfill each task's requirement. Such a book could not possibly be exhaustive
  25791. and the organizational problems would be daunting, but a good text like this
  25792. might be useful enough to be a starting point. Of course, as writers learn
  25793. from writing, programmers learn best from programming. There is still no
  25794. effective substitute for trial and error--many trials, many errors.
  25795. Sincerely,
  25796. Michael Nichols
  25797. 1725 York Avenue
  25798. New York, New York 10128
  25799. Thanks for your insightful comments. -- pjp
  25800. Editor:
  25801. I found Robert Watson's "DMA Controller Programming, in C" (CUJ, November
  25802. 1993) interesting, particularly of course the protected-mode details, but
  25803. found his conclusion, "the technique is actually very easy to use" laughable,
  25804. considering what he had just painstakingly documented. "Virtually impossible"
  25805. is more like it. I agree with his further conclusion: that DMA is increasingly
  25806. obsolete, since fast CPUs deal with dedicated card memory blocks more
  25807. expeditiously (and of course the implication that such memory is relatively
  25808. cheap in modern times -- DMA was originally a cost as well as speed feature).
  25809. I also think along the way he erroneously minimized the difficulties involved
  25810. in getting hold of a DMA buffer in real mode ("in real mode, generating a DMA
  25811. buffer is relatively easy"). I hope I'm missing something, but in my source
  25812. where I tried to accomplish this there are three or four failed attempts
  25813. commented-out, and I'm not at all happy with my current effort, shown in
  25814. Listing 2.
  25815. If anybody knows how to do this rationally, stop me before I code again! The
  25816. crude strategy here is to keep getting buffers, and if they're no good --
  25817. won't do DMA because they cross a physical boundary -- retain the offending
  25818. part of the buffer and keep trying; and then when we get a good one, free all
  25819. the chunks accumulated along the way. This approach relies on totally
  25820. unreliable assumptions about memory allocation, and really comes down to a
  25821. "keep trying until you give up" approach.
  25822. Operating systems in 80x86 land get to own low memory, where it is relatively
  25823. easy to set-up a good DMA buffer. Programs, on the other hand, can be loaded
  25824. at any address, and can't make any such arrangements except, as far as I can
  25825. figure out, by using awful like the example.
  25826. And incidentally, who owns the VDS (Virtual DMA Services) interface mentioned
  25827. in the article? I have a vague idea how one goes about getting in touch with
  25828. DPMI; is VDS one of these things that comes with DOS extenders and/or Windows
  25829. or what?
  25830. j.g. owen
  25831. Software engineer
  25832. 31 Darby Drive
  25833.  
  25834. South Huntington, NY 11746
  25835. cis 71121,625
  25836. To the Editor:
  25837. Concerning the letter from Lawrence H. Hardy in the 11.12 issue of CUJ, page
  25838. 136, asking for information about PCX graphics:
  25839. And I thought my memory was going... Actually, this might instead be seen as
  25840. an illustration of the need for a CUJ index -- depending on one's perspective
  25841. of course. At any rate, whilst fumbling through a stack of old Journals
  25842. seeking edification on a particular coding problem, I came across Vol. 9, No.
  25843. 8 (August, 1991). Emblazoned across the cover was "PCX GRAPHICS DOCUMENTED!"
  25844. And sure enough, there on page 89 was a decent discussion of the topic by one
  25845. Ian Ashdown, complete with bibliography.
  25846. I would also like to join Ian Somerton (p. 127) in expressing concern, if not
  25847. alarm, at the exponential increase in C++ coverage of late. Perhaps someone on
  25848. your staff misread it as C**?
  25849. Sincerely,
  25850. Scott Swanson
  25851. Box 75
  25852. Pendroy MT 59467
  25853. I am notorious within R&D for my instant amnesia, once an issue goes out the
  25854. door. You're right that an index to CUJ is indispensable, and the folks back
  25855. in Kansas have anticipated your wish. As for C++ coverage, that happens to be
  25856. an area of intense interest in our community at the moment. We haven't
  25857. forgotten about C by any means, but the mix what we publish is strongly
  25858. influenced by the mix of what's proposed to us in the way of articles. -- pjp
  25859.  
  25860. Listing 1 Creates "pointer mismatch" error message
  25861. char x;
  25862. char *xp = &x;
  25863. char **xpp = &xp;
  25864. void f(const char *cp)
  25865. {}
  25866. void g(const char **cpp)
  25867. {}
  25868. void a(void)
  25869. {
  25870. /* Call to 'g' gets pointer mismatch error */
  25871. f(xp);
  25872. g(xpp);
  25873. }
  25874.  
  25875. /* End of File */
  25876.  
  25877.  
  25878. Listing 2 Attempts to allocate a real-mode DMA buffer
  25879. #define BYTE char
  25880. typedef unsigned int WORD;
  25881. typedef unsigned long BIG;
  25882.  
  25883. #define JUNKCHUNK (128)
  25884. /* how we will search for physical
  25885. bounderies. (Must be >= sizeof(JUNK))
  25886. */
  25887.  
  25888. /* produce a big physical address. This
  25889. is "large" mode code; " FP_SEG" etc.
  25890. are
  25891. Turbo C macros which extract the two
  25892. parts of an 80x86-style segment-offset
  25893. pointer. */
  25894.  
  25895. #define PHYSICAL(x) ((BIG)((((BIG)
  25896. (FP_SEG(x)))<<4)+((BIG)FP_OFF(x))))
  25897.  
  25898. typedef struct JK
  25899. struct JK *next;
  25900. } JUNK;
  25901.  
  25902. _f void *mustallocdma(WORD size) {
  25903. BYTE *a;
  25904. JUNK root, *next, *p;
  25905. BIG leftover;
  25906. WORD junksize;
  25907.  
  25908. root.next=NULL;
  25909. next=&root;
  25910.  
  25911. while(1) {
  25912. a=mustalloc(size);
  25913. /* mustalloc==malloc,
  25914. but aborts if failure;
  25915. the function exists this
  25916. way or via the break below
  25917. at success.*/
  25918. if (
  25919. (leftover=(PHYSICAL(a) & 0xffff)) +
  25920. ((BIG) size)
  25921. >= 0x10000) {
  25922. /* it's no good. */
  25923. if (
  25924. (junksize = 0x10000-leftover)
  25925. < JUNKCHUNK)
  25926. junksize=JUNKCHUNK;
  25927. /* avoid endless
  25928. teeny-tiny manipulations. */
  25929.  
  25930. free(a);
  25931. next->next=mustalloc(junksize);
  25932. next=next->next;
  25933. }
  25934. else
  25935. break; /* success! */
  25936. }
  25937. /* free debris. */
  25938. next=root.next;
  25939. while(next)
  25940. p=next;
  25941. next=next->next;
  25942. free(p);
  25943. }
  25944. return a;
  25945. }
  25946.  
  25947. /* End of File */
  25948.  
  25949.  
  25950.  
  25951.  
  25952.  
  25953.  
  25954.  
  25955.  
  25956.  
  25957.  
  25958.  
  25959.  
  25960.  
  25961.  
  25962.  
  25963.  
  25964.  
  25965.  
  25966.  
  25967.  
  25968.  
  25969.  
  25970.  
  25971. Expanding a Conversation Processor for Time
  25972.  
  25973.  
  25974. Russell Suereth
  25975.  
  25976.  
  25977. Russell Suereth has been consulting for over 12 years in the New York City and
  25978. Boston areas. He started designing and coding systems on IBM mainframes and
  25979. now also builds PC software systems. You can write to Russell at 84 Old
  25980. Denville Rd, Boonton, NJ 07005. Russell Suereth is now completing a book, to
  25981. be published this summer by R&D Technical Books, entitled Processing Human
  25982. Conversations. He has extended his basic processor to deal with more complex
  25983. issues such as idioms, generating questions, and identifying general themes.
  25984.  
  25985.  
  25986. This article expands the language processor presented in "A Natural Language
  25987. Processor," CUJ, April 1993, to include time. That processor could accept
  25988. input sentences from a user, and within certain limited contexts, accurately
  25989. interpret their meaning. The processor can now recognize time words and
  25990. phrases to process more kinds of input sentences. I've included additional
  25991. processes for tense and number in the article. These additional processes help
  25992. clarify the meanings of ambiguous sentences, and generate an error response
  25993. when tense or number don't agree.
  25994. The code presented here is the part of the language processor that deals with
  25995. time. The complete processor is not shown here, but it is available on the
  25996. code disk.
  25997.  
  25998.  
  25999. Defining Time Words and Phrases
  26000.  
  26001.  
  26002. Time phrases are sequences of words that clarify when an event occurs. Without
  26003. time phrases the event can only be recognized as occurring in the past,
  26004. present, or future, by examining the tense of the sentence in which the event
  26005. occurs.
  26006. Time phrases identify events by specific time, elapsed time, or habitual
  26007. actions. Specific time is indicated by the day, month, year, or clock time.
  26008. The sentence "Jim runs at one o'clock" identifies the specific time Jim runs.
  26009. Elapsed time is indicated by a duration. The sentence "Jim runs for one hour"
  26010. identifies the length of time Jim runs. Habitual actions are indicated by
  26011. words such as "each" and "every." The sentence "Jim runs every day" identifies
  26012. the action Jim often performs.
  26013. A time word is a word that clarifies when an event occurs. The word may stand
  26014. on its own, or be part of a time phrase. Words such as "Tuesday," "midnight,"
  26015. and "morning" are time words, but the words "at," "in," and "last" are also
  26016. time words when used in a time phrase. The processor identifies time words by
  26017. matching the input sentence to an underlying structure.
  26018.  
  26019.  
  26020. Identifying Time Words and Phrases
  26021.  
  26022.  
  26023. Listing 1 contains the code for this article. The main routine calls the
  26024. check_underlying and check_time routines to identify time words in the input
  26025. sentence. The check_underlying routine matches the input sentence to the
  26026. underlying structures. If the match is successful, then the sentence is
  26027. processed. Time words are words that match an underlying structure and have a
  26028. TIME word type. The processor recognizes adjacent time words as a time phrase
  26029. and assigns the value TIMEPHRASE to these words. The check_time routine copies
  26030. the time phrase into the times array. The times array contains a time phrase
  26031. entry for each input sentence.
  26032.  
  26033.  
  26034. Deriving Time Meaning
  26035.  
  26036.  
  26037. The processor derives time meaning from specific time words in the input
  26038. sentence. Some time words and their meanings are shown in Table 1. When one of
  26039. these words occurs in a sentence and indicates time, that word's time meaning
  26040. is assigned to the sentence.
  26041. The main routine calls derive_time_meaning which looks at the input sentence's
  26042. time phrase. If a time phrase word matches a coded time word, then that word's
  26043. time meaning is assigned to the time_meaning array. The time_meaning array
  26044. contains a time meaning entry for each input sentence.
  26045.  
  26046.  
  26047. Interpreting Auxiliary Meaning
  26048.  
  26049.  
  26050. Many sentences contain auxiliary phrases which qualify the certainty of a
  26051. particular event's occurrence. Example phrases are "could be" and "must have
  26052. been." The auxiliary phrase helps the processor understand the sentence. On
  26053. the other hand, the sentence can't be fully understood if the auxiliary
  26054. meaning is unclear. Unclear auxiliary meaning occurs when the sentence has no
  26055. auxiliary, when the auxiliary has more than one meaning, or when the auxiliary
  26056. meaning is ambiguous. If the auxiliary meaning is unclear, the processor can
  26057. use the auxiliary and sentence tense to determine a clear meaning. Table 2
  26058. shows unclear auxiliary and tense combinations with their clear meaning.
  26059.  
  26060.  
  26061. Handling No Auxiliary
  26062.  
  26063.  
  26064. The auxiliary meaning is unclear when the sentence has no auxiliary. Sentences
  26065. with no auxiliary have an implied auxiliary meaning. The processor uses the
  26066. sentence tense to identify the implied meaning. For example, the sentence "Jim
  26067. ran in the race" has no auxiliary and is past tense. That sentence is similar
  26068. to another sentence with past tense "Jim had run in the race." The auxiliary
  26069. "had" means a particular point of time. When an input sentence has no
  26070. auxiliary and is past tense, the processor assigns the particular point of
  26071. time meaning to the sentence. In another example, the sentence "Jim runs in
  26072. the race" has no auxiliary and is present tense. That sentence is similar to
  26073. another sentence with present tense "Jim is running in the race." The
  26074. auxiliary "is," used in the present tense, means limited duration. When an
  26075. input sentence has no auxiliary and is present tense, the processor assigns
  26076. the limited duration meaning to the sentence.
  26077. The main routine calls derive_aux_meaning to derive a clear auxiliary meaning
  26078. from an unclear meaning. The derive_aux_meaning routine looks at the
  26079. auxiliaries entry for the sentence. If the auxiliary's string length is zero,
  26080. then the sentence has no auxiliary and the routine looks at the tenses entry.
  26081. If the tenses value is PAST, then PARTICULAR_POINT_OF_TIME is assigned to the
  26082. auxiliary meaning. If tenses is PRESENT, then LIMITED_DURATION is assigned to
  26083. the auxiliary meaning. If tenses is FUTURE, then the processor assigns
  26084. FIXED_PLAN to the auxiliary meaning.
  26085.  
  26086.  
  26087. Handling Ambiguous Auxiliaries
  26088.  
  26089.  
  26090. An ambiguous auxiliary is also potentially confusing to the processor. An
  26091. example is the auxiliary "could be" in the sentence "Jim could be running in
  26092. the race." In the present tense, "could be" has two meanings. That sentence in
  26093. the present tense means "Jim is able to run in the race," or "Jim is permitted
  26094. to run in the race." The auxiliary meaning remains ambiguous because "could
  26095. be" has two meanings in the present tense. In the future tense, "could be" has
  26096. one meaning. That sentence in the future tense means "It is possible that Jim
  26097. will run in the race." The auxiliary meaning is clarified because "could be"
  26098. has only one meaning in the future tense.
  26099. The derive_aux_meaning routine looks for specific ambiguous auxiliaries in the
  26100. sentence's auxiliaries entry. If a specific ambiguous auxiliary is found, then
  26101. the routine assigns a clear meaning to the sentence's auxiliary meaning. The
  26102. routine assigns a clear meaning based on the value in the sentence's tenses
  26103. entry.
  26104.  
  26105.  
  26106. Asking for Specific Meaning
  26107.  
  26108.  
  26109.  
  26110. The processor can clarify ambiguous auxiliary meaning when certain tenses are
  26111. used. But when the auxiliary meaning remains ambiguous, then the processor
  26112. generates a response that asks for more specific meaning. Figure 1 shows a
  26113. processor session with ambiguous auxiliary meaning in the input sentence.
  26114. The make_response routine calls ask_meaning to generate a response that asks
  26115. about ambiguous auxiliary meaning. The ask_meaning routine looks for specific
  26116. auxiliaries in the sentence's auxiliaries entry. If a specific auxiliary is
  26117. found, then the routine generates a response. The routine also looks at the
  26118. sentence's tenses and numbers entries. These entries help identify the
  26119. appropriate auxiliaries that can be used to make the response grammatical.
  26120.  
  26121.  
  26122. Error Handling
  26123.  
  26124.  
  26125. Time phrases can refer to an event in the past, present, or future. This time
  26126. reference must match the past, present, or future tense of the sentence. If
  26127. the time and tense conflict, then the sentence doesn't make sense and is
  26128. ungrammatical. An example is the sentence "Jim had run next week." The time
  26129. and tense don't match because "had run" refers to the past, while "next week"
  26130. refers to the future.
  26131. The make_response routine calls check_agreement to identify agreement errors
  26132. in the input sentence. The check_agreement routine looks at the time_meaning
  26133. and tenses entries for the input sentence. If the time_meaning entry is LAST,
  26134. and the tenses entry is PRESENT or FUTURE, then agreement_error is called to
  26135. generate an agreement error response. If the time_meaning entry is NEXT, and
  26136. the tenses entry is PAST, then agreement_error is called for the agreement
  26137. error response. The check_agreement routine also calls agreement_error when
  26138. either the sentence number or sentence tense are in error.
  26139. The processor generates an error response for three kinds of grammar conflicts
  26140. in the input sentence. A grammar conflict between time meaning and auxiliary
  26141. meaning is shown in the sentence "Jim will run last week." A grammar conflict
  26142. between auxiliary tense and verb tense is shown in the sentence "Jim will ran
  26143. in the race." A grammar conflict among subject, auxiliary, and verb number is
  26144. shown in the sentence "Jim are running in the race." The explanation response
  26145. gives a detailed reason why the input sentence is ungrammatical. Figure 2
  26146. shows the explanation responses for the above ungrammatical input sentences.
  26147. The check_agreement routine calls agreement_error to generate the explanation
  26148. response. The agreement_error routine is passed a value in the error_type
  26149. parameter that identifies the kind of grammar conflict. If the error_type is
  26150. TIME_MEANING_ERROR, then the time and auxiliary meaning conflict and the
  26151. routine generates an appropriate explanation response. If the error_type value
  26152. is TENSES_ERROR, then the auxiliary and verb tense conflict and an explanation
  26153. response is generated. If the error_type value is NUMBER_ERROR, then the
  26154. subject, auxiliary, and verb number conflict and an explanation response is
  26155. generated.
  26156.  
  26157.  
  26158. Responses Based on Meaning
  26159.  
  26160.  
  26161. The processor generates a response based on auxiliary meaning when the
  26162. sentence is grammatical. In the original processor the generated response was
  26163. a simple "OK." This expanded processor creates an interesting response based
  26164. on the auxiliary meaning. Given the input sentence "Jim is running in the
  26165. race," the processor assigns the limited duration meaning to the sentence. The
  26166. processor then generates the response "When will Jim stop running" based on
  26167. that auxiliary meaning. Table 3 shows auxiliary meanings and associated
  26168. example responses.
  26169. The make_response routine calls aux_meaning_response to generate a response
  26170. based on auxiliary meaning. The aux_meaning_response routine looks for
  26171. specific auxiliary meanings in the input sentence. If a specific auxiliary
  26172. meaning is found, then a response for that meaning is generated with words
  26173. from the input sentence. The routine also checks the sentence tense, number,
  26174. and subject type so appropriate words can be used to create a grammatical
  26175. response.
  26176.  
  26177.  
  26178. Summary
  26179.  
  26180.  
  26181. Several processes are required to process time. The processes described in
  26182. this article identify time phrases and words in the sentence; derive time
  26183. meaning from the sentence; derive a clear auxiliary meaning from an unclear
  26184. meaning; generate a response that asks about unclear meaning; determine time
  26185. and sentence tense agreement; generate a response that explains grammar
  26186. conflicts; and generate a response based on meaning.
  26187. Further expansions to the processor could compare one time to another; include
  26188. time when matching input sentences; generate interesting responses based on
  26189. time meaning; and allow ungrammatical sentences to be fully processed.
  26190. Processing time helps the processor identify grammatical sentences, and to
  26191. derive more meaning from the sentence. As a result, the processor can generate
  26192. responses that, at times, almost appear human.
  26193.  
  26194.  
  26195. Bibliography
  26196.  
  26197.  
  26198. Liles, Bruce L. 1971. An Introductory Transformational Grammar. Englewood
  26199. Cliffs: Prentice-Hall.
  26200. Quirk, Randolph, and Sidney Greenbaum. 1973. A Concise Grammar of Contemporary
  26201. English. San Diego: Harcourt Brace Jovanovich.
  26202. Suereth, Russell. "A Natural Language Processor." The C User's Journal. April,
  26203. 1993.
  26204. Suereth, Russell. "Natural Language Expansions for Tense and Number." The C
  26205. User's Journal. June, 1993.
  26206. Figure 1 A processor session when the input sentence has ambiguous auxiliary
  26207. meaning
  26208. Sentence: Jim can run in the race
  26209. Response: Do you mean
  26210.  Jim is able to run, or
  26211.  Jim is permitted to run
  26212.  
  26213. Sentence: Jim could have run in the race
  26214. Response: Do you mean
  26215.  Jim was able to run, or
  26216.  Jim was permitted to run
  26217.  
  26218. Sentence: Jim may run in the race
  26219. Response: Do you mean
  26220.  Jim will be permitted to run, or
  26221.  Jim will possibly run
  26222.  
  26223. Sentence: Jim will run in the race
  26224. Response: Do you mean
  26225.  Jim is willing to run, or
  26226.  Jim is intending to run, or
  26227.  You insist Jim will run, or
  26228.  You predict Jim will run
  26229. Figure 2 A processor session with ungrammatical input sentences
  26230. Sentence: Jim will run last week
  26231.  
  26232. Response: I don't understand, 'will' means future
  26233.  time, but 'last week' means past time
  26234.  
  26235. Sentence: Jim will ran in the race
  26236. Response: I don't understand, 'will' and that form
  26237.  of 'run' aren't used together
  26238.  
  26239. Sentence: Jim are running in the race
  26240. Response: I don't understand, 'are', 'Jim', and that
  26241.  form of 'run' don't agree in number
  26242. Table 1 Time words and meaning
  26243. Time Word Time Meaning
  26244. -------------------------
  26245. at Point of Time
  26246. on Day
  26247. in Period of Time
  26248. for Duration
  26249. before Before
  26250. by Before
  26251. after After
  26252. since Since
  26253. between Between
  26254. until Until
  26255. up to Until
  26256. last Last
  26257. next Next
  26258. each Each
  26259. every Every
  26260. all All
  26261. Table 2 The auxiliary and tense combinations below initially have an unclear
  26262. auxiliary meaning. But the processsor can determine a clear meaning through
  26263. sentence analysis.
  26264. Auxiliary Tense Clear Meaning
  26265. --------------------------------------------------
  26266. no auxiliary Past Particular Point of Time
  26267.  Present Limited Duration
  26268. is Present Limited Duration
  26269.  Future Fixed Plan
  26270. could Future Possibility
  26271. could be Future Possibility
  26272. may be Past Possibility
  26273.  Present Possibility
  26274. may have Past Possibility
  26275.  Present Possibility
  26276. may have been Past Possibility
  26277.  Present Possibility
  26278. would Past Characteristic
  26279. would have Past Characteristic
  26280. would have been Past Characteristic
  26281. would be any Probability
  26282. must have been any Logical Necessity
  26283. must any Obligation
  26284. must be any Obligation
  26285. Table 3 Example responses for auxiliary meaning in the input sentence
  26286. Auxiliary Meaning Example Response
  26287. in Input Sentence
  26288. --------------------------------------------------------------------
  26289. Limited Duration When will Jim stop running
  26290. Particular Point of Time When did Jim run
  26291. Up to Present Will Jim continue running
  26292. Not Completed When did Jim stop running
  26293.  
  26294. Possibility Is this highly possible
  26295. Probability Will this be highly possible
  26296. Obligation What happened when Jim didn't run
  26297. Characteristic How often did Jim run
  26298. Logical Necessity Why was it necessary that Jim was running
  26299. Fixed Plan Will Jim be ready to run
  26300.  
  26301. Listing 1 The expansion code for time
  26302. /* Copyright (c) 1993 Russell Suereth */
  26303.  
  26304. #include "natural.h"
  26305. #define PROBABILITY 220
  26306. #define CHARACTERISTIC 221
  26307. #define LOGICAL_NECESSITY 222
  26308. #define TENSES_ERROR 210
  26309. #define TIME_MEANING_ERROR 211
  26310. #define NUMBER_ERROR 212
  26311. #define POINT_OF_TIME 100
  26312. #define DAY 101
  26313. #define PERIOD_OF_TIME 102
  26314. #define DURATION 103
  26315. #define BEFORE 104
  26316. #define AFTER 105
  26317. #define SINCE 106
  26318. #define BETWEEN 107
  26319. #define UNTIL 108
  26320. #define LAST 111
  26321. #define NEXT 112
  26322. #define EACH 113
  26323. #define EVERY 114
  26324. #define ALL 115
  26325. int ask_meaning(void);
  26326. void check_time(void);
  26327. void derive_aux_meaning(void);
  26328. void derive_time_meaning(void);
  26329. int check_agreement(void);
  26330. void check_time(void);
  26331. void agreement_error(int);
  26332. void aux_meaning_response(void);
  26333. int check_underlying3(void);
  26334. char times[20][31];
  26335. unsigned char time_meaning[20];
  26336.  
  26337. /******************************************************/
  26338. /* Determine if the input sentence contains a known, */
  26339. /* underlying structure. If it does, then assign the */
  26340. /* correct types and phrases for the words. */
  26341. /******************************************************/
  26342. int check_underlying()
  26343. {
  26344. int i = 0;
  26345. /* Structure PRON-AUX-VERB-PREP-DET-NOUN */
  26346. if ( (check_type("PRON", i) == 0) &&
  26347. (check_type("AUX", i+1) == 0) &&
  26348. (check_type("VERB", i+2) == 0) &&
  26349. (check_type("PREP", i+3) == 0) &&
  26350. (check_type("DET", i+4) == 0) &&
  26351. (check_type("NOUN", i+5) == 0) ) {
  26352. strcpy(prime_types[i], "PRON");
  26353.  
  26354. strcpy(prime_types[i+1], "AUX");
  26355. strcpy(prime_types[i+2], "VERB");
  26356. strcpy(prime_types[i+3], "PREP");
  26357. strcpy(prime_types[i+4], "DET");
  26358. strcpy(prime_types[i+5], "NOUN");
  26359. strcpy(phrases[i], "NOUNPHRASE");
  26360. strcpy(phrases[i+1], "VERBPHRASE");
  26361. strcpy(phrases[i+2], "VERBPHRASE");
  26362. strcpy(phrases[i+3], "PREPPHRASE");
  26363. strcpy(phrases[i+4], "PREPPHRASE");
  26364. strcpy(phrases[i+5], "PREPPHRASE");
  26365. strcpy(auxiliaries[sentence], word_array[i+1]);
  26366. get_aux();
  26367. return(0):
  26368. }
  26369.  
  26370. /* Structure NAME-AUX-VERB-PREP-DET-NOUN */
  26371. if ( (check_type("NAME", i) == 0) &&
  26372. (check_type("AUX", i+1) == 0) &&
  26373. (check_type("VERB", i+2) == 0) &&
  26374. (check_type("PREP", i+3) == 0) &&
  26375. (check_type("DET", i+4) == 0) &&
  26376. (check_type("NOUN", i+5) == 0) ) {
  26377. strcpy(prime_types[i], "NAME");
  26378. strcpy(prime_types[i+1], "AUX");
  26379. strcpy(prime_types[i+2], "VERB");
  26380. strcpy(prime_types[i+3], "PREP");
  26381. strcpy(prime_types[i+4], "DET");
  26382. strcpy(prime_types[i+5], "NOUN");
  26383. strcpy(phrases[i], "NOUNPHRASE");
  26384. strcpy(phrases[i+1], "VERBPHRASE");
  26385. strcpy(phrases[i+2], "VERBPHRASE");
  26386. strcpy(phrases[i+3], "PREPPHRASE");
  26387. strcpy(phrases[i+4], "PREPPHRASE");
  26388. strcpy(phrases[i+5], "PREPPHRASE");
  26389. strcpy(auxiliaries[sentence], word_array[i+1]);
  26390. get_aux();
  26391. return(0);
  26392. }
  26393.  
  26394. /* Structure NAME-AUX-AUX-AUX-VERB-PREP-TIME-TIME*/
  26395. if ( (check_type("NAME", i) == 0) &&
  26396. (check_type("AUX", i+1) == 0) &&
  26397. (check_type("AUX", i+2) == 0) &&
  26398. (check_type("AUX", i+3) == 0) &&
  26399. (check_type("VERB", i+4) == 0) &&
  26400. (check_type("PREP", i+5) == 0) &&
  26401. (check_type("TIME", i+6) == 0) &&
  26402. (check_type("TIME", i+7) == 0) ) {
  26403. strcpy(prime_types[i], "NAME");
  26404. strcpy(prime_types[i+1], "AUX");
  26405. strcpy(prime_types[i+2], "AUX");
  26406. strcpy(prime_types[i+3], "AUX");
  26407. strcpy(prime_types[i+4], "VERB");
  26408. strcpy(prime_types[i+5], "TIME");
  26409. strcpy(prime_types[i+6], "TIME");
  26410. strcpy(prime_types[i+7], "TIME");
  26411. strcpy(phrases[i], "NOUNPHRASE");
  26412. strcpy(phrases[i+1], "VERBPHRASE");
  26413.  
  26414. strcpy(phrases[i+2], "VERBPHRASE");
  26415. strcpy(phrases[i+3], "VERBPHRASE");
  26416. strcpy(phrases[i+4], "VERBPHRASE");
  26417. strcpy(phrases[i+5], "TIMEPHRASE");
  26418. strcpy(phrases[i+6], "TIMEPHRASE");
  26419. strcpy(phrases[i+7], "TIMEPHRASE");
  26420. strcpy(auxiliaries[sentence], word_array[i+1]);
  26421. strcat(auxiliaries[sentence], " ");
  26422. strcat(auxiliaries[sentence], word_array[i+2]);
  26423. strcat(auxiliaries[sentence], " ");
  26424. strcat(auxiliaries[sentence], word_array[i+3]);
  26425. get_aux();
  26426. return(0);
  26427. }
  26428.  
  26429. /* Structure NAME-AUX-AUX-AUX-VERB-PREP-TIME */
  26430. if ( (check_type("NAME", i) == 0) &&
  26431. (check_type("AUX", i+1) == 0) &&
  26432. (check_type("AUX", i+2) == 0) &&
  26433. (check_type("AUX", i+3) == 0) &&
  26434. (check_type("VERB", i+4) == 0) &&
  26435. (check_type("PREP", i+5) == 0) &&
  26436. (check_type("TIME", i+6) == 0) ) {
  26437. strcpy(prime_types[i], "NAME");
  26438. strcpy(prime_types[i+1], "AUX");
  26439. strcpy(prime_types[i+2], "AUX");
  26440. strcpy(prime_types[i+3], "AUX");
  26441. strcpy(prime_types[i+4], "VERB");
  26442. strcpy(prime_types[i+5], "TIME");
  26443. strcpy(prime_types[i+6], "TIME");
  26444. strcpy(phrases[i], "NOUNPHRASE");
  26445. strcpy(phrases[i+1], "VERBPHRASE");
  26446. strcpy(phrases[i+2], "VERBPHRASE");
  26447. strcpy(phrases[i+3], "VERBPHRASE");
  26448. strcpy(phrases[i+4], "VERBPHRASE");
  26449. strcpy(phrases[i+5], "TIMEPHRASE");
  26450. strcpy(phrases[i+6], "TIMEPHRASE");
  26451. strcpy(auxiliaries[sentence], word_array[i+1]);
  26452. strcat(auxiliaries[sentence], " ");
  26453. strcat(auxiliaries[sentence], word_array[i+2]);
  26454. strcat(auxiliaries[sentence], " ");
  26455. strcat(auxiliaries[sentence], word_array[i+3]);
  26456. get_aux();
  26457. return(0);
  26458. }
  26459.  
  26460. /* Structure NAME-AUX-AUX-AUX-VERB-TIME-TIME */
  26461. if ( (check_type("NAME", i) == 0) &&
  26462. (check_type("AUX", i+1) == 0) &&
  26463. (check_type("AUX", i+2) == 0) &&
  26464. (check_type("AUX", i+3) == 0) &&
  26465. (check_type("VERB", i+4) == 0) &&
  26466. (check_type("TIME", i+5) == 0) &&
  26467. (check_type("TIME", i+6) == 0) ) {
  26468. strcpy(prime_types[i], "NAME");
  26469. strcpy(prime_types[i+1], "AUX");
  26470. strcpy(prime_types[i+2], "AUX");
  26471. strcpy(prime_types[i+3], "AUX");
  26472. strcpy(prime_types[i+4], "VERB");
  26473.  
  26474. strcpy(prime_types[i+5], "TIME");
  26475. strcpy(prime_types[i+6], "TIME");
  26476. strcpy(phrases[i], "NOUNPHRASE");
  26477. strcpy(phrases[i+1], "VERBPHRASE");
  26478. strcpy(phrases[i+2], "VERBPHRASE");
  26479. strcpy(phrases[i+3], "VERBPHRASE");
  26480. strcpy(phrases[i+4], "VERBPHRASE");
  26481. strcpy(phrases[i+5], "TIMEPHRASE");
  26482. strcpy(phrases[i+6], "TIMEPHRASE");
  26483. strcpy(auxiliaries[sentence], word_array[i+1]);
  26484. strcat(auxiliaries[sentence], " ");
  26485. strcat(auxiliaries[sentence], word_array[i+2]);
  26486. strcat(auxiliaries[sentence], " ");
  26487. strcat(auxiliaries[sentence], word_array[i+3]);
  26488. get_aux();
  26489. return(0);
  26490. }
  26491.  
  26492. /* Structure NAME-AUX-AUX-VERB-PREP-TIME-TIME */
  26493. if ( (check_type("NAME", i) == 0) &&
  26494. (check_type("AUX", i+1) == 0) &&
  26495. (check_type("AUX", i+2) == 0) &&
  26496. (check_type("VERB", i+3) == 0) &&
  26497. (check_type("PREP", i+4) == 0) &&
  26498. (check_type("TIME", i+5) == 0) &&
  26499. (check_type("TIME", i+6) == 0) ) {
  26500. strcpy(prime_types[i], "NAME");
  26501. strcpy(prime_types[i+1], "AUX");
  26502. strcpy(prime_types[i+2], "AUX");
  26503. strcpy(prime_types[i+3], "VERB");
  26504. strcpy(prime_types[i+4], "TIME");
  26505. strcpy(prime_types[i+5], "TIME");
  26506. strcpy(prime_types[i+6], "TIME");
  26507. strcpy(phrases[i], "NOUNPHRASE");
  26508. strcpy(phrases[i+1], "VERBPHRASE");
  26509. strcpy(phrases[i+2], "VERBPHRASE");
  26510. strcpy(phrases[i+3], "VERBPHRASE");
  26511. strcpy(phrases[i+4], "TIMEPHRASE");
  26512. strcpy(phrases[i+5], "TIMEPHRASE");
  26513. strcpy(phrases[i+6], "TIMEPHRASE");
  26514. strcpy(auxiliaries[sentence], word_array[i+1]);
  26515. strcat(auxiliaries[sentence], " ");
  26516. strcat(auxiliaries[sentence], word_array[i+2]);
  26517. get_aux();
  26518. return(0);
  26519. }
  26520.  
  26521. /* Structure NAME-AUX-AUX-VERB-PREP-TIME */
  26522. if ( (check_type("NAME", i) == 0) &&
  26523. (check_type("AUX", i+1) == 0) &&
  26524. (check_type("AUX", i+2) == 0) &&
  26525. (check_type("VERB", i+3) == 0) &&
  26526. (check_type("PREP", i+4) == 0) &&
  26527. (check_type("TIME", i+5) == 0) ) {
  26528. strcpy(prime_types[i], "NAME");
  26529. strcpy(prime_types[i+1], "AUX");
  26530. strcpy(prime_types[i+2], "AUX");
  26531. strcpy(prime_types[i+3], "VERB");
  26532. strcpy(prime_types[i+4], "TIME");
  26533.  
  26534. strcpy(prime_types[i+5], "TIME");
  26535. strcpy(phrases[i], "NOUNPHRASE");
  26536. strcpy(phrases[i+1], "VERBPHRASE");
  26537. strcpy(phrases[i+2], "VERBPHRASE");
  26538. strcpy(phrases[i+3], "VERBPHRASE");
  26539. strcpy(phrases[i+4]. "TIMEPHRASE");
  26540. strcpy(phrases[i+5], "TIMEPHRASE");
  26541. strcpy(auxiliaries[sentence], word_array[i+1]);
  26542. strcat(auxiliaries[sentence], " ");
  26543. strcat(auxiliaries[sentence], word_array[i+2]);
  26544. get_aux();
  26545. return(0);
  26546. }
  26547.  
  26548. /* Structure NAME-AUX-AUX-VERB-TIME-TIME */
  26549. if ( (check_type("NAME", i) == 0) &&
  26550. (check_type("AUX", i+1) == 0) &&
  26551. (check_type("AUX", i+2) == 0) &&
  26552. (check_type("VERB", i+3) == 0) &&
  26553. (check_type("TIME", i+4) == 0) &&
  26554. (check_type("TIME", i+5) == 0) ) {
  26555. strcpy(prime_types[i], "NAME");
  26556. strcpy(prime_types[i+1], "AUX");
  26557. strcpy(prime_types[i+2], "AUX");
  26558. strcpy(prime_types[i+3], "VERB");
  26559. strcpy(prime_types[i+4], "TIME");
  26560. strcpy(prime_types[i+5], "TIME");
  26561. strcpy(phrases[i], "NOUNPHRASE");
  26562. strcpy(phrases[i+1], "VERBPHRASE");
  26563. strcpy(phrases[i+2], "VERBPHRASE");
  26564. strcpy(phrases[i+3], "VERBPHRASE");
  26565. strcpy(phrases[i+4], "TIMEPHRASE");
  26566. strcpy(phrases[i+5], "TIMEPHRASE");
  26567. strcpy(auxiliaries[sentence], word_array[i+1]);
  26568. strcat(auxiliaries[sentence], " ");
  26569. strcat(auxiliaries[sentence], word_array[i+2]);
  26570. get_aux();
  26571. return(0):
  26572. }
  26573.  
  26574. /* Structure NAME-AUX-VERB-PREP-TIME-TIME */
  26575. if ( (check_type("NAME", i) == 0) &&
  26576. (check_type("AUX", i+1) == 0) &&
  26577. (check_type("VERB", i+2) == 0) &&
  26578. (check_type("PREP", i+3) == 0) &&
  26579. (check_type("TIME", i+4) == 0) &&
  26580. (check_type("TIME", i+5) == 0) ) {
  26581. strcpy(prime_types[i], "NAME");
  26582. strcpy(prime_types[i+1], "AUX");
  26583. strcpy(prime_types[i+2], "VERB");
  26584. strcpy(prime_types[i+3], "TIME");
  26585. strcpy(prime_types[i+4], "TIME");
  26586. strcpy(prime_types[i+5], "TIME");
  26587. strcpy(phrases[i], "NOUNPHRASE");
  26588. strcpy(phrases[i+1], "VERBPHRASE");
  26589. strcpy(phrases[i+2], "VERBPHRASE");
  26590. strcpy(phrases[i+3], "TIMEPHRASE");
  26591. strcpy(phrases[i+4], "TIMEPHRASE");
  26592. strcpy(phrases[i+5], "TIMEPHRASE");
  26593.  
  26594. strcpy(auxiliaries[sentence], word_array[i+1]);
  26595. get_aux();
  26596. return(0);
  26597. }
  26598.  
  26599. /* Structure NAME-AUX-VERB-PREP-TIME */
  26600. if ( (check_type("NAME", i) == 0) &&
  26601. (check_type("AUX", i+1) == 0) &&
  26602. (check_type("VERB", i+2) == 0) &&
  26603. (check_type("PREP", i+3) == 0) &&
  26604. (check_type("TIME", i+4) == 0) ) {
  26605. strcpy(prime_types[i], "NAME");
  26606. strcpy(prime_types[i+1], "AUX");
  26607. strcpy(prime_types[i+2], "VERB");
  26608. strcpy(prime_types[i+3], "TIME");
  26609. strcpy(prime_types[i+4], "TIME");
  26610. strcpy(phrases[i], "NOUNPHRASE");
  26611. strcpy(phrases[i+1], "VERBPHRASE");
  26612. strcpy(phrases[i+2], "VERBPHRASE");
  26613. strcpy(phrases[i+3], "TIMEPHRASE");
  26614. strcpy(phrases[i+4], "TIMEPHRASE");
  26615. strcpy(auxiliaries[sentence], word_array[i+1]):
  26616. get_aux();
  26617. return(0);
  26618. }
  26619.  
  26620. /* Structure NAME-AUX-VERB-TIME-TIME */
  26621. if ( (check_type("NAME", i) == 0) &&
  26622. (check_type("AUX", i+1) == 0) &&
  26623. (check_type("VERB", i+2) == 0) &&
  26624. (check_type("TIME", i+3) == 0) &&
  26625. (check_type("TIME", i+4) == 0) ) {
  26626. strcpy(prime_types[i], "NAME");
  26627. strcpy(prime_types[i+1], "AUX");
  26628. strcpy(prime_types[i+2], "VERB");
  26629. strcpy(prime_types[i+3], "TIME");
  26630. strcpy(prime_types[i+4], "TIME");
  26631. strcpy(phrases[i], "NOUNPHRASE"):
  26632. strcpy(phrases[i+1], "VERBPHRASE");
  26633. strcpy(phrases[i+2], "VERBPHRASE");
  26634. strcpy(phrases[i+3], "TIMEPHRASE");
  26635. strcpy(phrases[i+4], "TIMEPHRASE");
  26636. strcpy(auxiliaries[sentence], word_array[i+1]);
  26637. get_aux();
  26638. return(0);
  26639. }
  26640. return(1);
  26641. }
  26642.  
  26643. /*****************************************************/
  26644. /* If the phrase is a "TIMEPHRASE", then all the */
  26645. /* words in the phrase refer to a time. Concatenate */
  26646. /* these words to the times array. */
  26647. /*****************************************************/
  26648. void check_time()
  26649. {
  26650. int i;
  26651. for (i=0; i<word_ct; i++) {
  26652. if (strcmp(phrases[i], "TIMEPHRASE") == 0) {
  26653.  
  26654. if (strlen(times[sentence]) > 0)
  26655. strcat(times[sentence], " ");
  26656. strcat(times[sentence], word_array[i]);
  26657. }
  26658. }
  26659. return;
  26660. }
  26661.  
  26662. /*****************************************************/
  26663. /* Generate a response with information from a */
  26664. /* matching, previous sentence. */
  26665. /*****************************************************/
  26666. void_make_response()
  26667. {
  26668. int i;
  26669.  
  26670. if (check_agreement() != 0) return;
  26671.  
  26672. if (strcmpi(word_array[0], "where") != 0) {
  26673. if (ask_meaning() != 0) aux_meaning_response();
  26674. return;
  26675. }
  26676.  
  26677. /***************************************************/
  26678. /* Match subject, action, tense, and meaning. */
  26679. /***************************************************/
  26680. for (i=sentence-1; i>=0; i--) {
  26681. if ((strcmpi(subjects[i],subjects[sentence])==0) &&
  26682. (strcmpi(actions[i], actions[sentence]) ==0) &&
  26683. (strlen(places[i]) > 0) &&
  26684. (tenses[i] == tenses[sentence]) &&
  26685. (strpbrk(aux_meaning[i],aux_meaning[sentence])
  26686. != NULL)) {
  26687. make_answer(i);
  26688. return;
  26689. }
  26690. }
  26691. /***************************************************/
  26692. /* Match subject, action, and tense. */
  26693. /***************************************************/
  26694. for (i=sentence-1; i>=0; i--) {
  26695. if ((strcmpi(subjects[i],subjects[sentence])==0) &&
  26696. (strcmpi(actions[i], actions[sentence]) ==0) &&
  26697. (strlen(places[i]) > 0) &&
  26698. (tenses[i] == tenses[sentence])) {
  26699. make_answer(i);
  26700. return;
  26701. }
  26702. }
  26703.  
  26704. /***************************************************/
  26705. /* Match subject, action, and meaning. */
  26706. /***************************************************/
  26707. for (i=sentence-1; i>=0; i--) {
  26708. if ((strcmpi(subjects[i],subjects[sentence])==0) &&
  26709. (strcmpi(actions[i], actions[sentence]) ==0) &&
  26710. (strlen(places[i]) > 0) &&
  26711. (strpbrk(aux_meaning[i],aux_meaning[sentence])
  26712. != NULL)) {
  26713.  
  26714. strcpy(response, "I'm not sure, but ");
  26715. make_answer(i);
  26716. return;
  26717. }
  26718. }
  26719. /***************************************************/
  26720. /* Match subject and action. */
  26721. /***************************************************/
  26722. for (i=sentence-1; i>=0; i--) {
  26723. if ((strcmpi(subjects[i],subjects[sentence])==0) &&
  26724. (strcmpi(actions[i], actions[sentence]) ==0) &&
  26725. (strlen(places[i]) > 0)) {
  26726. strcpy(response, "I'm not sure, but ");
  26727. make_answer(i);
  26728. return;
  26729. }
  26730. }
  26731. strcpy(response, "I don't know");
  26732. return;
  26733. }
  26734.  
  26735. /*****************************************************/
  26736. /* Obtain a clear auxiliary meaning from an */
  26737. /* ambiguous meaning. */
  26738. /*****************************************************/
  26739.  
  26740. void derive_aux_meaning()
  26741. {
  26742. if (strlen(auxiliaries[sentence]) == 0) {
  26743. if (tenses[sentence] == PAST)
  26744. aux_meaning[sentence][0] =
  26745. PARTICULAR_POINT_OF_TIME;
  26746. if (tenses[sentence] == PRESENT)
  26747. aux_meaning[sentence][0]= LIMITED_DURATION;
  26748. if (tenses[sentence] == FUTURE)
  26749. aux_meaning[sentence][0]= FIXED_PLAN;
  26750. }
  26751.  
  26752. if (strcmpi(auxiliaries[sentence], "is") == 0) {
  26753. if (tenses[sentence] == PRESENT) {
  26754. memset(aux_meaning[sentence], '\0', 5);
  26755. aux_meaning[sentence][0]= LIMITED_DURATION;
  26756. }
  26757. if (tenses[sentence] == FUTURE) {
  26758. memset(aux_meaning[sentence], '\0', 5);
  26759. aux_meaning[sentence][0]= FIXED_PLAN;
  26760. }
  26761. }
  26762.  
  26763. if ( ((strcmpi(auxiliaries[sentence],
  26764. "could" == 0) 
  26765. (strcmpi(auxiliaries[sentence],
  26766. "could be") == 0)) &&
  26767. (tenses[sentence] == FUTURE)) {
  26768. memset(aux_meaning[sentence], '\0', 5);
  26769. aux_meaning[sentence][0]= POSSIBILITY;
  26770. }
  26771.  
  26772. if ( ((strcmpi(auxiliaries[sentence],
  26773.  
  26774. "may be") == 0) 
  26775. (strcmpi(auxiliaries[sentence],
  26776. "may have") == 0) 
  26777. (strcmpi(auxiliaries[sentence],
  26778. "may have been") == 0)) &&
  26779. ((tenses[sentence] == PAST) 
  26780. (tenses[sentence] == PRESENT)) ) {
  26781. memset(aux_meaning[sentence], '\0', 5);
  26782. aux_meaning[sentence][0] = POSSIBILITY;
  26783. }
  26784.  
  26785. if ( ((strcmpi(auxiliaries[sentence],
  26786. "would") == 0) 
  26787. (strcmpi(auxiliaries[sentence],
  26788. "would have") ==0) 
  26789. (strcmpi(auxiliaries[sentence],
  26790. "would have been") == 0)) &&
  26791. (tenses[sentence] == PAST)) {
  26792. memset(aux_meaning[sentence], '\0', 5);
  26793. aux-meaning[sentence][0] = CHARACTERISTIC;
  26794. }
  26795.  
  26796. if (strcmpi(auxiliaries[sentence],
  26797. "would be") == 0) {
  26798. memset(aux_meaning[sentence], '\0', 5);
  26799. aux_meaning[sentence][0] = PROBABILITY;
  26800. }
  26801.  
  26802. if (strcmpi(auxiliaries[sentence],
  26803. "must have been") == 0) {
  26804. memset(aux_meaning[sentence], '\0', 5);
  26805. aux_meaning[sentence][0]= LOGICAL_NECESSITY;
  26806. }
  26807.  
  26808. if ((strcmpi(auxiliaries[sentence], "must") == 0) 
  26809. (strcmpi(auxiliaries[sentence],
  26810. "must be") == 0)) {
  26811. memset(aux_meaning[sentence], '\0', 5);
  26812. aux_meaning[sentence][0] = OBLIGATION;
  26813. }
  26814.  
  26815. return;
  26816. }
  26817.  
  26818. /*****************************************************/
  26819. /* Obtain the meaning of time words in the sentence. */
  26820. /*****************************************************/
  26821. void derive_time_meaning()
  26822. {
  26823. int i;
  26824. for (i=0; i<word_ct; i++) {
  26825. if (strcmp(phrases[i],"TIMEPHRASE") == 0) {
  26826. if (strcmpi(word_array[i], "at") == 0)
  26827. time_meaning[sentence] = POINT_OF_TIME;
  26828. if (strcmpi(word_array[i], "on") == 0)
  26829. time_meaning[sentence] = DAY;
  26830. if (strcmpi(word_array[i], "in") == 0)
  26831. time_meaning[sentence] = PERIOD_OF_TIME;
  26832. if (strcmpi(word_array[i], "for") == 0)
  26833.  
  26834. time_meaning[sentence] = DURATION;
  26835. if (strcmpi(word_array[i], "before") == 0)
  26836. time_meaning[sentence] = BEFORE;
  26837. if (strcmpi(word_array[i], "after") == 0)
  26838. time_meaning[sentence] = AFTER;
  26839. if (strcmpi(word_array[i], "since") == 0)
  26840. time_meaning[sentence] = SINCE;
  26841. if (strcmpi(word_array[i], "until") == 0)
  26842. time_meaning[sentence] = UNTIL;
  26843. if (strcmpi(word_array[i], "between") == 0)
  26844. time_meaning[sentence] = BETWEEN;
  26845. if (strcmpi(word_array[i], "by") == 0)
  26846. time_meaning[sentence] = BEFORE;
  26847. if (strcmpi(word_array[i], "up to") == 0)
  26848. time_meaning[sentence] = UNTIL;
  26849. if (strcmpi(word_array[i], "last") == 0)
  26850. time_meaning[sentence] = LAST;
  26851. if (strcmpi(word_array[i], "next") == 0)
  26852. time_meaning[sentence] = NEXT;
  26853. if (strcmpi(word_array[i], "each") == 0)
  26854. time_meaning[sentence] = EACH;
  26855. if (strcmpi(word_array[i], "every") == 0)
  26856. time_meaning[sentence] = EVERY;
  26857. if (strcmpi(word_array[i], "all") == 0)
  26858. time_meaning[sentence] = ALL;
  26859. break;
  26860. }
  26861. }
  26862. return;
  26863. }
  26864.  
  26865. /****************************************************/
  26866. /* Create a response that asks for more specific */
  26867. /* meaning. */
  26868. /****************************************************/
  26869. int ask_meaning()
  26870. {
  26871. if ((strcmpi(auxiliaries[sentence],
  26872. "can") == 0) 
  26873. (strcmpi(auxiliaries[sentence],
  26874. "can be") == 0) 
  26875. (strcmpi(auxiliaries[sentence],
  26876. "can have") == 0)) {
  26877. strcpy(response, "Do you mean\n\t\t");
  26878. subjects[sentence][0] =
  26879. (char) toupper(subjects[sentence][0]);
  26880. strcat(response, subjects[sentence]);
  26881.  
  26882. if (tenses[sentence] == PRESENT) {
  26883. if (numbers[sentence] == SINGULAR)
  26884. strcat(response, " is");
  26885. else strcat(response, "are");
  26886. }
  26887.  
  26888. if (tenses[sentence] == FUTURE)
  26889. strcat(response, "will be");
  26890.  
  26891. strcat(response, " able to ");
  26892. strcat(response, actions[sentence]);
  26893.  
  26894. strcat(response, " or,\n\t\t");
  26895. strcat(response, subjects[sentence]);
  26896.  
  26897. if (tenses[sentence] == PRESENT) {
  26898. if (numbers[sentence] == SINGULAR)
  26899. strcat(response, " is");
  26900. else strcat(response, "are");
  26901. }
  26902.  
  26903. if (tenses[sentence] == FUTURE)
  26904. strcat(response, "will be");
  26905.  
  26906. strcat(response, " permitted to ");
  26907. strcat(response, actions[sentence]);
  26908. return(0);
  26909. }
  26910.  
  26911. if ( ((strcmpi(auxiliaries[sentence],
  26912. "could") == 0) 
  26913. (strcmpi(auxiliaries[sentence],
  26914. "could be") == 0) 
  26915. (strcmpi(auxiliaries[sentence],
  26916. "could have") == 0) 
  26917. (strcmpi(auxiliaries[sentence],
  26918. "could have been") == 0)) &&
  26919. ((tenses[sentence] == PAST) 
  26920. (tenses[sentence] == PRESENT)) ) {
  26921.  
  26922. strcpy(response, "Do you mean\n\t\t");
  26923. subjects[sentence][0] =
  26924. (char) toupper(subjects[sentence][0]);
  26925. strcat(response, subjects[sentence]);
  26926.  
  26927. if (tenses[sentence] == PAST) {
  26928. if (numbers[sentence] == SINGULAR)
  26929. strcat(response, " was");
  26930. else strcat(response, " were");
  26931. }
  26932.  
  26933. if (tenses[sentence] == PRESENT) {
  26934. if (numbers[sentence] == SINGULAR)
  26935. strcat(response, " is");
  26936. else strcat(response, "are");
  26937. }
  26938.  
  26939. strcat(response, " able to ");
  26940. strcat(response, actions[sentence]);
  26941. strcat(response, " or,\n\t\t");
  26942. strcat(response, subjects[sentence]);
  26943.  
  26944. if (tenses[sentence] == PAST) {
  26945. if (numbers[sentence] == SINGULAR)
  26946. strcat(response, " was");
  26947. else strcat(response, " were");
  26948. }
  26949.  
  26950. if (tenses[sentence] == PRESENT) {
  26951. if (numbers[sentence] == SINGULAR)
  26952. strcat(response, " is");
  26953.  
  26954. else strcat(response, " are");
  26955. }
  26956.  
  26957. strcat(response, " permitted to ");
  26958. strcat(response, actions[sentence]);
  26959. return(0);
  26960. }
  26961.  
  26962. if ( ((strcmpi(auxiliaries[sentence],
  26963. "may") == 0) 
  26964. (strcmpi(auxiliaries[sentence],
  26965. "may be") == 0)) &&
  26966. (tenses[sentence] == FUTURE) ) {
  26967.  
  26968. strcpy(response, "Do you mean\n\t\t");
  26969. subjects[sentence][0] =
  26970. (char) toupper(subjects[sentence][0]);
  26971. strcat(response, subjects[sentence]);
  26972. strcat[response, " will be permitted to ");
  26973. strcat(response, actions[sentence]);
  26974. strcat(response, " or,\n\t\t");
  26975. strcat(response, subjects[sentence]);
  26976. strcat(response, " will possibly ");
  26977. strcat(response, actions[sentence]);
  26978. return(0);
  26979. }
  26980.  
  26981. if ((strcmpi(auxiliaries[sentence], "will") == 0 ) 
  26982. (strcmpi(auxiliaries[sentence],
  26983. "will be") == 0)) {
  26984.  
  26985. strcpy(response, "Do you mean\n\t\t");
  26986. subjects[sentence][0] =
  26987. (char) toupper(subjects[sentence][0]);
  26988. strcat(response, subjects[sentence]);
  26989.  
  26990. if (numbers[sentence] == SINGULAR)
  26991. strcat(response, " is");
  26992. else strcat(response, " are");
  26993.  
  26994. strcat(response, " willing to ");
  26995. strcat(response, actions[sentence]);
  26996. strcat(response, " or,\n\t\t");
  26997. strcat(response, subjects[sentence]);
  26998.  
  26999. if (numbers[sentence] == SINGULAR)
  27000. strcat(response, " is");
  27001. else strcat(response, " are");
  27002.  
  27003. strcat[response, " intending to ");
  27004. strcat(response, actions[sentence]);
  27005. strcat(response, " or,\n\t\t");
  27006.  
  27007. strcat(response, "You insist ");
  27008. if (subjects_type[sentence] == PRONOUN)
  27009. subjects[sentence][0] = (char)
  27010. (char) tolower(subjects[sentence][0]);
  27011. strcat(response, subjects[sentence]);
  27012. strcat(response, " will ");
  27013.  
  27014. strcat(response, actions[sentence]);
  27015. strcat(response, " or,\n\t\t");
  27016.  
  27017. strcat(response, "You predict ");
  27018. if (subjects_type[sentence] == PRONOUN)
  27019. subjects[sentence][0] = (char)
  27020. (char) tolower(subjects[sentence][0]);
  27021. strcat(response, subjects[sentence]);
  27022. strcat(response, " will ");
  27023. strcat(response, actions[sentence]);
  27024. return(0);
  27025. }
  27026. return(1);
  27027. }
  27028.  
  27029. /****************************************************/
  27030. /* Create a response based on the meaning of the */
  27031. /* auxiliary in the sentence. */
  27032. /****************************************************/
  27033. void aux_meaning_response()
  27034. {
  27035. switch (aux_meaning[sentence][0]) {
  27036. case LIMITED_DURATION:
  27037. strcpy(response, "When will ");
  27038. if (subjects_type[sentence] == PRONOUN)
  27039. subjects[sentence][0] = (char)
  27040. tolower(subjects[sentence][0]);
  27041. strcat(response, subjects[sentence]);
  27042. strcat(response, " stop ");
  27043. get_verb(tenses[sentence],
  27044. numbers[sentence], 'I');
  27045. break;
  27046.  
  27047. case PARTICULAR_POINT_OF_TIME:
  27048. strcpy(response, "When did ");
  27049. if (subjects_type[sentence] == PRONOUN)
  27050. subjects[sentence][0] = (char)
  27051. tolower(subjects[sentence][0]);
  27052. strcat(response, subjects[sentence]);
  27053. strcat(response, " ");
  27054. strcat(response, actions[sentence]);
  27055. break;
  27056.  
  27057. case UP_TO_PRESENT:
  27058. strcpy(response, "Will ");
  27059. if (subjects_type[sentence] == PRONOUN)
  27060. subjects[sentence][0] = (char)
  27061. tolower(subjects[sentence][0]);
  27062. strcat(response, subjects[sentence]);
  27063. strcat(response, " continue ");
  27064. get_verb(tenses[sentence],
  27065. numbers[sentence], 'I');
  27066. break;
  27067.  
  27068. case NOT_COMPLETED:
  27069. strcpy(response, "When did ");
  27070. if (subjects_type[sentence] == PRONOUN)
  27071. subjects[sentence][0] = (char)
  27072. tolower(subjects[sentence][0]);
  27073.  
  27074. strcat(response, subjects[sentence]);
  27075. strcat(response, "stop ");
  27076. get_verb(tenses[sentence],
  27077. numbers[sentence], 'I');
  27078. break;
  27079.  
  27080. case POSSIBILITY:
  27081. if (tenses[sentence] == PAST)
  27082. strcat(response, "Was this ");
  27083. if (tenses[sentence] == PRESENT)
  27084. strcat(response, "Is this ");
  27085. if (tenses[sentence] == FUTURE)
  27086. strcat(response, "Will this be ");
  27087. strcat(response, "highly possible");
  27088. break;
  27089.  
  27090. case PROBABILITY:
  27091. strcpy(response,
  27092. "Will this be highly probable");
  27093. break;
  27094.  
  27095. case OBLIGATION:
  27096. strcpy(response, "What ");
  27097.  
  27098. if (tenses[sentence] == PAST)
  27099. strcat(response, "happened when ");
  27100. if (tenses[sentence] == FUTURE)
  27101. strcat(response, "will happen if ");
  27102.  
  27103. if (subjects_type[sentence] == PRONOUN)
  27104. subjects[sentence][0] = (char)
  27105. tolower(subjects[sentence][0]);
  27106. strcat(response, subjects[sentence]);
  27107.  
  27108. if (tenses[sentence] == PAST)
  27109. strcat(response, " didn't ");
  27110. if (tenses[sentence] == FUTURE)
  27111. strcat(response, " doesn't ");
  27112.  
  27113. strcat(response, actions[sentence]);
  27114. break;
  27115.  
  27116. case CHARACTERISTIC:
  27117. strcpy(response, "How often did ");
  27118. if (subjects_type[sentence] == PRONOUN)
  27119. subjects[sentence][0] = (char)
  27120. tolower(subjects[sentence][0]);
  27121. strcat(response, subjects[sentence]);
  27122. strcat(response, " ");
  27123. strcat(response, actions[sentence]);
  27124. break;
  27125.  
  27126. case LOGICAL_NECESSITY:
  27127. strcpy(response,
  27128. "Why was it necessary that ");
  27129. if (subjects_type[sentence] == PRONOUN)
  27130. subjects[sentence][0] = (char)
  27131. tolower(subjects[sentence][0]);
  27132. strcat(response, subjects[sentence]);
  27133.  
  27134. if (numbers[sentence] == SINGULAR)
  27135. strcat(response, " was ");
  27136. else strcat(response, " were ");
  27137.  
  27138. get_verb(tenses[sentence],
  27139. numbers[sentence], 'I');
  27140. break;
  27141.  
  27142. case FIXED_PLAN:
  27143. strcpy(response, "Will ");
  27144. if (subjects_type[sentence] == PRONOUN)
  27145. subjects[sentence][0] = (char)
  27146. tolower(subjects[sentence][0]);
  27147. strcat(response, subjects[sentence]);
  27148. strcat(response, " be ready to ");
  27149. strcat(response, actions[sentence]);
  27150. break;
  27151.  
  27152. default: /* all others */
  27153. strcpy(response, "OK");
  27154. break;
  27155. }
  27156. }
  27157.  
  27158. /****************************************************/
  27159. /* Determine if the input sentence words conflict. */
  27160. /****************************************************/
  27161. int check_agreement()
  27162. {
  27163. if (time_meaning[sentence] == LAST) {
  27164. if ( (tenses[sentence] == PRESENT) 
  27165. (tenses[sentence] == FUTURE) ) {
  27166. agreement_error(TIME_MEANING_ERROR);
  27167. return(1);
  27168. }
  27169. }
  27170.  
  27171. if (time_meaning[sentence] == NEXT) {
  27172. if (tenses[sentence] == PAST) {
  27173. agreement_error(TIME_MEANING_ERROR);
  27174. return(1);
  27175. }
  27176. }
  27177.  
  27178. if (numbers[sentence] == UNKNOWN) {
  27179. agreement_error(NUMBER_ERROR);
  27180. return(1);
  27181. }
  27182.  
  27183. if (tenses[sentence] == UNKNOWN) {
  27184. agreement_error(TENSES_ERROR);
  27185. return(1);
  27186. }
  27187.  
  27188. return(0);
  27189. }
  27190.  
  27191. /****************************************************/
  27192. /* Generate a response that explains the time */
  27193.  
  27194. /* agreement error. */
  27195. /****************************************************/
  27196. void agreement_error(int error_type)
  27197. {
  27198. int i;
  27199. if (error_type == TIME_MEANING_ERROR) {
  27200. strcpy(response, "I don't understand, '");
  27201. strcat(response, auxiliaries[sentence]);
  27202. strcat(response, "' means ");
  27203. for (i=0; (aux_tense[i] != ' ') &&
  27204. (aux_tense[i]); i++) {
  27205. if (i > 0) strcat(response, " or ");
  27206. if (aux_tense[i] == PAST)
  27207. strcat(response, "past");
  27208. if (aux_tense[i] == PRESENT)
  27209. strcat(response, "present");
  27210. if (aux_tense[i] == FUTURE)
  27211. strcat(response, "future");
  27212. }
  27213.  
  27214. strcat(response, " time, but '");
  27215. strcat(response, times[sentence]);
  27216. strcat(response, "' means ");
  27217. if (time_meaning[sentence] == LAST)
  27218. strcat(response, "past");
  27219. if (time_meaning[sentence] == NEXT)
  27220. strcat(response, "future");
  27221. strcat(response, " time ");
  27222. }
  27223.  
  27224. if (error_type == NUMBER_ERROR) {
  27225. strcpy(response, "I don't understand, '");
  27226. strcat(response, auxiliaries[sentence]);
  27227. strcat(response, "', '");
  27228. strcat(response, subjects[sentence]);
  27229. strcat(response, "', and that form of '");
  27230. strcat(response, actions[sentence]);
  27231. strcat(response, "' don't agree in number");
  27232. }
  27233.  
  27234. if (error_type == TENSES_ERROR) {
  27235. strcpy(response, "I don't understand, '");
  27236. strcat(response, auxiliaries[sentence]);
  27237. strcat(response, "' and that form of '");
  27238. strcat(response, actions[sentence]);
  27239. strcat(response, "' aren't used together");
  27240. }
  27241.  
  27242. return;
  27243. }
  27244.  
  27245. /* End of file */
  27246.  
  27247.  
  27248.  
  27249.  
  27250.  
  27251.  
  27252.  
  27253.  
  27254.  
  27255.  
  27256.  
  27257.  
  27258.  
  27259.  
  27260.  
  27261.  
  27262.  
  27263.  
  27264.  
  27265.  
  27266.  
  27267.  
  27268.  
  27269.  
  27270.  
  27271.  
  27272.  
  27273.  
  27274.  
  27275.  
  27276.  
  27277.  
  27278.  
  27279.  
  27280.  
  27281.  
  27282.  
  27283.  
  27284.  
  27285.  
  27286.  
  27287.  
  27288.  
  27289.  
  27290.  
  27291.  
  27292.  
  27293.  
  27294.  
  27295.  
  27296.  
  27297.  
  27298.  
  27299.  
  27300.  
  27301.  
  27302.  
  27303.  
  27304.  
  27305.  
  27306.  
  27307.  
  27308.  
  27309.  
  27310.  
  27311.  
  27312.  
  27313.  
  27314.  
  27315.  
  27316.  
  27317. Creating Spin Controls for Windows
  27318.  
  27319.  
  27320. Keith E. Bugg
  27321.  
  27322.  
  27323. Keith Bugg is a principal at Tristar Systems, Inc., which provides software
  27324. consultants and technical training for Windows and DEC platforms. Direct
  27325. queries to CompuServe ID 72203,3612.
  27326.  
  27327.  
  27328. Users of MS-Windows programs often need a convenient way to input a value that
  27329. falls within a certain range. For example, users of a process control system
  27330. may need to set the temperature of a vessel within a range of 100 to 200 F.
  27331. Because of their simplicity and intuitive feel, spin controls often provide
  27332. the best user interface in such applications; a spin control is one that
  27333. allows the user to change an input value by clicking on an up or down arrow
  27334. (or other appropriate icon). Clicking the up arrow increases the value;
  27335. clicking the down arrow decreases it. As the arrows are clicked, the new value
  27336. is displayed to the user via an edit box control and/or a graphical image
  27337. (e.g., the temperature could be displayed as a thermometer).
  27338. In this article, I present a simple implementation of spin controls in C++. A
  27339. sample program accompanies the text and should serve as a jumping-off point
  27340. for future exploration. The sample program's spin controls allow the user to
  27341. specify a date within a given range. The program demonstrates one of the
  27342. benefits of spin controls: they can prevent the user from entering invalid
  27343. inputs. In this example, spin controls keep the user from entering an invalid
  27344. date (including leap years).
  27345.  
  27346.  
  27347. Creating Spin Controls
  27348.  
  27349.  
  27350. From a Windows programming perspective, a spin control consists of three
  27351. fundamental parts: a read-only text box for displaying the current value, an
  27352. up arrow control for increasing the value, and a down arrow control for
  27353. decreasing the value. These controls can be placed on the screen in either a
  27354. vertical or horizontal orientation. Figure 1 illustrates some typical spin
  27355. controls. (The reason the text box is marked read-only is to prevent the user
  27356. from clicking in the box and directly entering a value.) The controls are
  27357. implemented as bit maps. Most Windows development kits supply a plethora of
  27358. bit maps, among which are the familiar up/down arrows used here. By making a
  27359. connection between an object (the edit box) and an event (a mouse click within
  27360. the outlines of a bit map) you create a spin control.
  27361.  
  27362.  
  27363. Making the Connection
  27364.  
  27365.  
  27366. In essence, spin control software is simply a matter of managing rectangles on
  27367. the screen. The connections between the edit box and its controls are
  27368. established by the following steps:
  27369. 1. Load into memory the control bit maps using LoadBitmap; note sizes.
  27370. 2. Create a memory Device Context (DC) via CreateCompatibleDC.
  27371. 3. Select bit maps into memory DC via SelectObject;
  27372. 4. Copy bitmaps using BitBlt to the output DC; note their positions.
  27373. 5. Trap left mouse click event; test if cursor was inside bit map.
  27374. 6. Process accordingly; update edit box as necessary.
  27375. Save the bit map sizes and positions and map these into RECT objects; the API
  27376. function PtInRect can then indicate if the mouse scores a "hit."
  27377.  
  27378.  
  27379. The Sample Program
  27380.  
  27381.  
  27382. The sample Windows program demonstrates the preceding actions in more detail.
  27383. This program builds a main window with a menu bar containing one option. When
  27384. selected, the option invokes a dialog box which is associated with the date
  27385. processor (see Figure 2). The function of most interest is DateDialog; the
  27386. other stuff is mostly the grunt work to build the window, menu, etc. When the
  27387. dialog box is initialized, the program creates edit boxes as child windows and
  27388. maps them to the dialog box; these edit boxes show the current day and year,
  27389. respectively. The program creates a combobox for the month and populates it
  27390. with the names of the twelve months. (The months are not sorted alphabetically
  27391. because the index into the combobox equals the numerical month; thus January
  27392. is 0, February is 1, etc.) Next, the program loads the bit maps for the up and
  27393. down arrows and saves each bit map's width and height in variables. The
  27394. program then creates a memory device context by calling CreateCompatibleDC.
  27395. The date processor is initialized with a default date of 15-Jan-1993. Finally,
  27396. the program defines the bit maps as rectangles, and maps them to the screen.
  27397. (These rectangles have been hard-coded for simplicity's sake.)
  27398. The Windows messages of major interest here are WM_PAINT, WM_LBUTTONDOWN, and
  27399. WM_RBUTTONDOWN. WM_PAINT causes the current value of the date to be displayed,
  27400. while the button messages effect changes in the date values. The program
  27401. responds to WM_PAINT by selecting the bit map objects (both the up and down
  27402. arrows, in sequence) into the memory DC created earlier; these objects are
  27403. then copied to the screen's DC via the BitBlt function.
  27404. The message handlers for the buttons are much more involved, but they have
  27405. more to do. First, the handlers record the position of the mouse cursor at the
  27406. time of the click. The program calls the Windows API function PtInRect to
  27407. determine if the cursor is in one of the rectangles occupied by a bit map. If
  27408. the cursor lies within a bit map region, the current date value is incremented
  27409. or decremented. Range checking is also enforced here; note the usual nasty
  27410. code that checks for leap years.
  27411. The right button down message handler requires even more code, since it
  27412. supports year "slewing." Slewing refers to the ability to update the value
  27413. continuously by holding the mouse button down while in one of the bit maps.
  27414. The slewing works as follows: once the program know the cursor is in one of
  27415. the year control arrows, it starts another loop. This new loop calls
  27416. PeekMessage to see what's in the pipeline. This loop immediately gets the
  27417. mouse position -- if the user has moved out of the rectangle, the year stops
  27418. slewing (even if the button is still down). Also, if the program finds a
  27419. WM_RBUTTONUP message, the loop breaks and the year stops slewing.
  27420. The rest of the program is concerned with housekeeping chores and exiting.
  27421. Most noteworthy in this regard are the DeleteDC and DeleteObject functions.
  27422. These functions free up the memory locked for loading the bit maps and the
  27423. memory DC.
  27424. I built this program with Borland's Turbo C++ v 3.1, but it should port easily
  27425. to most Windows-SDK-type platforms. To build this program, you will need to
  27426. create your own MAKE file and include SPIN.C, SPIN.RC, and SPIN.DEF. These
  27427. correspond to Listing 1, Listing 2 and Listing 3, respectively.
  27428.  
  27429.  
  27430. Ups and Downs of Spin Controls
  27431.  
  27432.  
  27433. As a Windows programmer, spin controls can save you a lot of work while
  27434. increasing software reliability. This reliability results from a simple
  27435. property of spin controls -- values are selected, not entered. This property
  27436. allows your program to easily enforce ranges, data typing, defaulting, and
  27437. other ammenities.
  27438. Spin controls can also have a down side. For starters, the target platform
  27439. must have a mouse. Writing a keyboard interface is clearly possible, but
  27440. messy. Also, the value being updated should span a relatively narrow range. It
  27441. would be poor design to force the user to click through 1000 values, for
  27442. example. A slider control would be more appropriate for values with a wide
  27443. range.
  27444.  
  27445.  
  27446. Final Remarks
  27447.  
  27448.  
  27449. For certain Windows programs, spin controls can provide a very user-friendly
  27450. control that pays extra dividends by way of increasing quality and
  27451. reliability. Likely candidates are process control systems, multi-media
  27452. applications (e.g., volume controls), and data base front ends. The
  27453. construction of spin controls by the technique described in this article lends
  27454. itself quite readily to object orientation, or at least to a DLL.
  27455. Spin controls are widely available from Microsoft and other vendors in the
  27456. guise of Visual Basic Custom Controls (VBXs). I've found these to be reliable
  27457. and cost-effective; if you do a lot of development work, you may be better off
  27458. to purchase a VBX. One caveat: VBXs will not be supported under Windows NT (in
  27459. their current form, at least), so pick your battles carefully. You might want
  27460. to hedge your bet and encapsulate the concepts of the sample program into a
  27461. C++ class for spin controls.
  27462.  
  27463.  
  27464.  
  27465. Bibliography
  27466.  
  27467.  
  27468. The algorithm for the leap year calculations is from The C Programming
  27469. Language by Kernighan & Ritchie, 2nd. edition.
  27470. Figure I Typical spin controls
  27471. Figure 2 Spin controls in a dialog box
  27472.  
  27473. Listing 1 A demo program that implements spin controls
  27474. // **************************************************
  27475. // * NAME = SPIN.C *
  27476. // * DEV. PLATFORM= Turbo C++,v3.1 for Windows *
  27477. // * MEMORY MODEL = SMALL *
  27478. // * Demo. program for creating/using spin controls.*
  27479. // * Keith E. Bugg, TRISTAR SYSTEMS, Inc *
  27480. // **************************************************
  27481. #define OEMRESOURCE // MUST define BEFORE <windows.h>
  27482. #define DAY_CTRL 100// Ctrl IDs for day editbox ctrl.
  27483. #define MONTH_CTRL 200 // ID for month combobox ctrl.
  27484. #define YEAR_CTRL 300 // ID for year editbox ctrl.
  27485. #include <windows.h>// must have Windows header file
  27486. #include <stdio.h> // get basic prototypes
  27487. #include <stdlib.h> // for 'itoa()', etc.
  27488.  
  27489. /* declare prototypes here */
  27490. long FAR PASCAL _export SpinProc(HWND, unsigned,
  27491. WORD, LONG);
  27492. BOOL FAR PASCAL DateDialog(HWND ,WORD,WORD ,LONG );
  27493. //
  27494. // GLOBAL VARIABLES HERE
  27495. //
  27496. HDC hDC; // handle to output Display Context
  27497. int day,month,year;
  27498. char *months[]= {"January","Febuary","March","April",
  27499. "May","June","July","August","September","October",
  27500. "November" ,"December"};
  27501. int max_days[]= {31,28,31,30,31,30,31,31.30,31,30,31};
  27502. char dayval[3],yearval[5];
  27503. // **************************************************
  27504. // end var. decl., etc. Now do message handler for
  27505. // the main window of the spin control demo program
  27506. // **************************************************
  27507. //
  27508. long FAR PASCAL_export SpinProc(HWND hWnd,
  27509. unsigned message,WORD wParam, LONG lParam)
  27510. {
  27511. PAINTSTRUCT ps; // Paint Struct for BeginPaint call
  27512. int i; // general purpose variable
  27513. HINSTANCE hInstance;
  27514. FARPROC lpfnDlgProc;
  27515. // --------- end local variables ---------------
  27516. //
  27517. switch (message) // process messages here
  27518. {
  27519.  
  27520. case WM_CLOSE: // Exit via system menu
  27521. MessageBeep(0); // Warning beep
  27522. i= MessageBox(hWnd,
  27523. "Are you sure you want to Exit?",
  27524. "EXIT",MB_OKCANCEL MB_ICONEXCLAMATION);
  27525.  
  27526. if(i == IDOK) // really wants to exit
  27527. { //queue up a QUIT msg
  27528. PostMessage(hWnd,WM_QUIT,0,0); 
  27529. return 0L;
  27530. }
  27531. break;
  27532.  
  27533. case WM_COMMAND: // check for system message
  27534. switch(wParam)
  27535. {
  27536. case SC_MINIMIZE: // on minimize
  27537. ShowWindow(hWnd,SW_SHOWMINIMIZED);
  27538. break;
  27539.  
  27540. case SC_MAXIMIZE: // on maximize
  27541. MessageBeep(0); //stub
  27542. ShowWindow(hWnd,SW_SHOWMAXIMIZED);
  27543. break;
  27544.  
  27545. case SC_RESTORE: // on restore
  27546. ShowWindow(hWnd,SW_SHOW);
  27547. break;
  27548. //
  27549. // here is user clicks the sub-menu
  27550. // option "Run Demo" from drop-down
  27551. case 100: // Run Demo menu option
  27552. hInstance = GetWindowWord(hWnd,
  27553. GWW_HINSTANCE);
  27554. lpfnDlgProc= MakeProcInstance(
  27555. DateDialog,hInstance);
  27556. DialogBox(hInstance,"DIALOG_1",
  27557. hWnd,lpfnDlgProc);
  27558. break;
  27559.  
  27560. default:
  27561. break;
  27562. }
  27563. break;
  27564.  
  27565. case WM_QUIT: // QUIT & DESTROY messages
  27566. case WM_DESTROY:
  27567. return (0L);
  27568.  
  27569. default: // message is of no interest
  27570. return (DefWindowProc(hWnd, message,
  27571. wParam, lParam));
  27572. }
  27573. return (NULL);
  27574. } /* end SpinProc() */
  27575.  
  27576. #pragma argsused // TC++ compiler directive
  27577. //
  27578. // next comes WinMain, which builds demo window
  27579. //
  27580. // ****************************************************
  27581. int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE
  27582. hPrevInstance,LPSTR lpCmdLine, int nCmdShow)
  27583. {
  27584. MSG msg; // message
  27585.  
  27586. WNDCLASS wc;
  27587. static HWND hWnd;
  27588. //
  27589. //------------------------------------------------
  27590. //
  27591.  
  27592. if (!hPrevInstance) // Other instances running?
  27593. {
  27594. // Fill in window class structure with
  27595. // parameters that describe the main window.
  27596. //
  27597. wc.style = CS_HREDRAW CS_VREDRAW;
  27598. wc.lpfnWndProc=(long(FAR PASCAL*)())SpinProc;
  27599. wc.cbClsExtra = 0; // No per-class extra data.
  27600. wc.cbWndExtra = 0; // No per-window extra data.
  27601. wc.hInstance = hInstance;
  27602. wc.hIcon = LoadIcon(hInstance, "spin");;
  27603. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  27604. wc.hbrBackground = CreateSolidBrush(
  27605. RGB(100,255,255)); // cyan background
  27606. wc.lpszMenuName = "MAIN_MENU";
  27607. wc.lpszClassName = "SPIN";
  27608.  
  27609. // Register the window class and return
  27610. // success/failure code.
  27611.  
  27612. if(!RegisterClass(&wc))
  27613. return 0;
  27614.  
  27615. // Create main window for application instance.
  27616.  
  27617. hWnd = CreateWindow(
  27618. "SPIN", // See RegisterClass() call.
  27619. "Spin Controls",// Text for title bar.
  27620. WS_MAXIMIZE WS_SYSMENU WS_MINIMIZEBOX 
  27621. WS_MAXIMIZEBOX WS_THICKFRAME 
  27622. WS_OVERLAPPEDWINDOW,// Window's styles..
  27623. 0, // horizontal position.
  27624. 0, // vertical position.
  27625. 600, // Default width.
  27626. 400, // Default height.
  27627. NULL, // no parent.
  27628. NULL, // No menu bar (later)
  27629. hInstance, // inst. owns window.
  27630. NULL // Pointer not needed.
  27631. );
  27632. //
  27633. // If CreateWindow failed, return "failure" */
  27634. if (!hWnd)
  27635. {
  27636. MessageBeep(0);
  27637. MessageBox(hWnd,"Could not create window!",
  27638. "ERROR",MB_OK);
  27639. return 0;
  27640. }
  27641.  
  27642. // Make window visible; update its client area
  27643.  
  27644. nCmdShow= SW_SHOWNORMAL; // Show normal size
  27645.  
  27646. ShowWindow(hWnd,nCmdShow ); // Show the window
  27647. UpdateWindow(hWnd); // Send WM_PAINT
  27648.  
  27649. }
  27650. // Acquire and dispatch messages until a
  27651. // WM_QUIT message is received.
  27652.  
  27653. while (GetMessage(&msg, // message structure
  27654. NULL, // window handle rec. msg
  27655. NULL, // lowest msg. to examine
  27656. NULL)) // highest msg. to examine
  27657. {
  27658. TranslateMessage(&msg); // Translates msgs.
  27659. DispatchMessage(&msg); // Dispatches msgs.
  27660. }
  27661. return (msg.wParam); // Returns the value
  27662. // from PostQuitMessage
  27663. } // end WinMain()
  27664.  
  27665. BOOL FAR PASCAL DateDialog(HWND hDlg,WORD wMessage,
  27666. WORD wParam, LONG lParam)
  27667. {
  27668. static HWND hDay,hCombo,hYear;
  27669. HANDLE hInstance; // app. instance
  27670. //
  27671. // decl. rectangles for bit maps
  27672. //
  27673. static RECT day_up,day_down,year_up, year_down;
  27674. static HDC hMemDC; // handle to memory DC
  27675. static BITMAP bm; // for loading bit maps
  27676. static HBITMAP hUp,hDown; // bit map handles
  27677. MSG msg; // message struct. for slewing
  27678. int k, mon_max; // mon_max is max # days in month
  27679. static int bmWidth, bmHeight; // bit map size
  27680. POINT pt; // for finding mouse click in rects.
  27681. //
  27682. switch(wMessage)
  27683. {
  27684. // initialize dialog box here
  27685. case WM_INITDIALOG:
  27686. hInstance = GetWindowWord(hDlg,
  27687. GWW_HINSTANCE);
  27688. //
  27689. // create Day edit box as child window
  27690. //
  27691. hDay = CreateWindow("EDIT",NULL,ES_LEFT 
  27692. ES_READONLY WS_CHILD WS_BORDER
  27693.  WS_VISIBLE WS_TABSTOP,40,55,50,
  27694. 25,hDlg,DAY_CTRL,hInstance,NULL);
  27695. //
  27696. // create Month combobox as child window
  27697. //
  27698. hCombo = CreateWindow("COMBOBOX",NULL,
  27699. WS_CHILD WS_VSCROLL WS_TABSTOP
  27700.  CBS_DROPDOWN CBS_HASSTRINGS,
  27701. 175, 55, 120, 150,hDlg,MONTH_CTRL,
  27702. hInstance,NULL);
  27703. //
  27704. // create Year edit box as child window
  27705.  
  27706. //
  27707. hYear = CreateWindow("EDIT",NULL,ES_LEFT 
  27708. ES_READONLY WS_CHILD WS_VISIBLE
  27709.  WS_BORDER WS_TABSTOP,
  27710. 310,55,70,25,hDlg,YEAR_CTRL,
  27711. hInstance,NULL);
  27712. ShowWindow(hYear,SW_SHOWNORMAL);
  27713. //
  27714. // init. combobox with names of months
  27715. //
  27716. for(k=0; k < 12; k++)
  27717. SendMessage(hCombo,CB_ADDSTRING,0,
  27718. (DWORD)(LPSTR)months[k]);
  27719.  
  27720. ShowWindow(hCombo,SW_SHOWNORMAL);
  27721. //
  27722. // Load the Microsoft-supplied arrows here
  27723. //
  27724. hUp = LoadBitmap(NULL,OBM_UPARROW);
  27725. hDown = LoadBitmap(NULL,OBM_DNARROW);
  27726. //
  27727. // Get the height & width of the up/down
  27728. // arrows supplied by Microsoft. Use
  27729. // this to set up rectangles upon which
  27730. // the arrows will be "pasted". When
  27731. // user clicks mouse inside one of these
  27732. // rectangles, we change the day/year
  27733. //
  27734. GetObject(hUp, sizeof(BITMAP), &bm);
  27735. // get size of up/down arrow (same)
  27736. bmWidth = bm.bmWidth;
  27737. bmHeight = bm.bmHeight;
  27738. //
  27739. // memory handle to DC; !** IMPORTANT **!
  27740. //
  27741. hMemDC = CreateCompatibleDC(hDC);
  27742. //
  27743. // init. the "Day" and "Year" edit boxes
  27744. // set to date 15-Jan-1993
  27745. //
  27746. day= 15; // set day to 15
  27747. SetDlgItemText(hDlg,DAY_CTRL,"15");
  27748. //
  27749. // do month here
  27750. //
  27751. month= 0; // January
  27752. SendMessage(hCombo,CB_SELECTSTRING,0,
  27753. (DWORD)(LPSTR)months[month]);
  27754. //
  27755. //
  27756. year= 1993; // set year to 1993
  27757.  
  27758. SetDlgItemText(hDlg,YEAR_CTRL,"1993");
  27759. //
  27760. // define rect. for the "Day" up arrow
  27761. // ***********************************
  27762. day_up.left=105;
  27763. day_up.top= 56;
  27764. day_up.right= 105+bmWidth;
  27765.  
  27766. day_up.bottom= day_up.top+bmHeight;
  27767. //
  27768. // now do down arrow for "Day"
  27769. //
  27770. day_down.left=105;
  27771. day_down.top= 74;
  27772. day_down.right= 105+bmWidth;
  27773. day_down.bottom= day_down.top+bmHeight;
  27774. //
  27775. // define rect. for the "Year" up arrow
  27776. //
  27777. year_up.left= 395;
  27778. year_up.top= 56;
  27779. year_up.right= 395+bmWidth;
  27780. year_up.bottom= year_up.top+bmHeight;
  27781. //
  27782. // do down arrow for "Year"
  27783. //
  27784. year_down.left= 395;
  27785. year_down.top= 74;
  27786. year_down.right= 395+bmWidth;
  27787. year_down.bottom= year_down.top+bmHeight;
  27788.  
  27789. return (TRUE);
  27790. //
  27791. // do paint message here
  27792. //
  27793. case WM_PAINT:
  27794. //
  27795. // get handle to display context
  27796. //
  27797. hDC= GetDC(hDlg);
  27798. //
  27799. // now "paste" the up/down arrow bitmap
  27800. // into position. Note: the coordinates
  27801. // are hard-coded, not as parameters
  27802. //
  27803. SelectObject(hMemDC,hUp);
  27804. BitBlt(hDC,105,56,bmWidth,bmHeight,hMemDC,
  27805. 0,0,SRCCOPY); // day up
  27806. BitBlt(hDC,395,56,bmWidth,bmHeight,hMemDC,
  27807. 0,0,SRCCOPY); // year up
  27808. //
  27809. SelectObject(hMemDC,hDown);
  27810. BitBlt(hDC,105,74,bmWidth,bmHeight,hMemDC,
  27811. 0,0,SRCCOPY); // day down
  27812. BitBlt(hDC,395,74,bmWidth,bmHeight,hMemDC,
  27813. 0,0,SRCCOPY); // year down
  27814.  
  27815. ReleaseDC(hDlg,hDC); // release DC
  27816. break;
  27817. //
  27818. // end paint message
  27819. //
  27820. case WM_CLOSE:
  27821. //
  27822. // *** Release memory back to Windows ***
  27823. //
  27824. DeleteDC(hMemDC);
  27825.  
  27826. DeleteObject(hUp);
  27827. DeleteObject(hDown);
  27828. EndDialog(hDlg,0); // close dialog box
  27829. return (TRUE);
  27830. //
  27831. // user clicked left button here
  27832. //
  27833. case WM_LBUTTONDOWN:
  27834. pt.x = LOWORD(lParam);
  27835. pt.y = HIWORD(lParam);
  27836. //
  27837. // Was click in day up bit map?
  27838. //
  27839. if(PtInRect(&day_up,pt))
  27840. {
  27841. ++day; // user wants to increase day
  27842. //
  27843. // get month, then max days for that
  27844. // month.check for leap year, too
  27845. //
  27846. month = (WORD) SendMessage(hCombo,
  27847. CB_GETCURSEL,0,0L);
  27848. mon_max= max_days[month];
  27849. //
  27850. // check for leap year here
  27851. //
  27852. if(month == 1) // 0=Jan, 1= Feb, etc
  27853. {
  27854. // need to get year
  27855. if((year % 4 == 0 && year % 100
  27856. != 0) year % 400 ==0)
  27857. max_days[1]= 29;// lp yr.
  27858. }
  27859. if(day > mon_max)
  27860. day = 1;
  27861. itoa(day,dayval,10);
  27862. // Update edit box with new day
  27863. SetDlgItemText(hDlg,DAY_CTRL,dayval);
  27864. }
  27865. //
  27866. // see if click in day down arrow
  27867. //
  27868. if(PtInRect(&day_down,pt))
  27869. {
  27870. --day; // user wants to decrease day
  27871. if(day < 1)
  27872. day = 1;
  27873. itoa(day,dayval,10);
  27874. // Update edit box with new day
  27875. SetDlgItemText(hDlg,DAY_CTRL,dayval);
  27876. }
  27877. //
  27878. // see if click in year up arrow
  27879. //
  27880. if(PtInRect(&year_up,pt))
  27881. {
  27882. ++year; // incr. year & check range
  27883. if(year > 2200)
  27884. year = 2200;
  27885.  
  27886. itoa(year,yearval,10);
  27887. // Update edit box with new year
  27888. SetDlgItemText(hDlg,YEAR_CTRL,yearval);
  27889. }
  27890. //
  27891. // see if click in year down arrow
  27892. //
  27893. if(PtInRect(&year_down,pt))
  27894. {
  27895. --year; // decr. year & check range
  27896. if(year < 1800)
  27897. year = 1800;
  27898. itoa(year,yearval,10);
  27899. // Update edit box with new year
  27900. SetDlgItemText(hDlg,YEAR_CTRL,yearval);
  27901. }
  27902. break;
  27903.  
  27904. case WM_RBUTTONDOWN: // right button slewing
  27905. pt.x = LOWORD(lParam);
  27906. pt.y = HIWORD(lParam);
  27907. //
  27908. // Is cursor in year up rectangle?
  27909. //
  27910. if(PtInRect(&year_up,pt))
  27911. {
  27912. while(1)
  27913. {
  27914. if(PeekMessage(&msg,NULL,0,0,
  27915. PM_REMOVE))
  27916. {
  27917. pt = msg.pt;
  27918. ScreenToClient(hDlg,&pt);
  27919. if(!PtInRect(&year_up,pt))
  27920. { // user moved out of box
  27921. break;
  27922. }
  27923. if(msg.message== WM_RBUTTONUP)
  27924. break;
  27925. else
  27926. {
  27927. TranslateMessage(&msg);
  27928. DispatchMessage(&msg);
  27929. }
  27930. }
  27931. ++year; // increment year
  27932. if(year > 2200) // check range
  27933. year = 1993;// default to 1993
  27934. itoa(year,yearval,10);
  27935. // Update edit box with new year
  27936. SetDlgItemText(hDlg,YEAR_CTRL,
  27937. yearval);
  27938.  
  27939. } // end while loop for slewing
  27940.  
  27941. } // end if slewed year up
  27942. //
  27943. // now check for slewing year down
  27944. //
  27945.  
  27946. if(PtInRect(&year_down,pt))
  27947. {
  27948. while(1)
  27949. {
  27950. if(PeekMessage(&msg,NULL,0,0,
  27951. PM_REMOVE))
  27952. {
  27953. pt = msg.pt;
  27954. ScreenToClient(hDlg,&pt);
  27955. if(!PtInRect(&year_down,pt))
  27956. { // user moved out of box
  27957. break;
  27958. }
  27959. if(msg.message== WN_RBUTTONUP)
  27960. break;
  27961. else
  27962. {
  27963. TranslateMessage(&msg);
  27964. DispatchMessage(&msg);
  27965. }
  27966. }
  27967. --year; // decrement year
  27968. if(year < 1800) // check range
  27969. year = 1800;
  27970. itoa(year,yearval,10);
  27971. // Update edit box with new year
  27972. SetDlgItemText(hDlg,YEAR_CTRL,
  27973. yearval);
  27974. } // end while loop for slewing down
  27975. } // end if year down was slewed
  27976.  
  27977. break;
  27978. } // end switch on messages
  27979. return (FALSE);
  27980. } // end DateDialog()
  27981. //
  27982. // end spin.c
  27983. //
  27984. // End of File
  27985.  
  27986.  
  27987. Listing 2 Resource file for spin control program
  27988. // Name = SPIN.RC
  27989. // Resource file for spin control demo program.
  27990. // **************************************************
  27991. //
  27992. // build dialog box for date processor w/spin control
  27993. //
  27994. DIALOG_1 DIALOG 18, 18, 231, 127
  27995. STYLE DS_MODALFRAME WS_POPUP WS_CAPTION
  27996.  WS_SYSMENU
  27997. CAPTION "DATE PROCESSOR"
  27998. BEGIN
  27999.  
  28000. LTEXT "Day", -1, 20, 15, 16, 8, WS_CHILD 
  28001. WS_VISIBLE WS_GROUP
  28002. LTEXT "Year", -1, 170, 15, 16, 8, WS_CHILD 
  28003. WS_VISIBLE WS_GROUP
  28004. LTEXT "Month", -1, 96, 15, 25, 8, WS_CHILD 
  28005.  
  28006. WS_VISIBLE WS_GROUP
  28007. END
  28008. //
  28009. // build main menu bar resource
  28010. //
  28011. MAIN_MENU MENU
  28012. BEGIN
  28013. POPUP "&Date"
  28014. BEGIN
  28015. MENUITEM "&Run Demo", 100
  28016. END
  28017.  
  28018. END
  28019. //
  28020. // end Listing 2, SPIN.RC
  28021. //
  28022. // End of File
  28023.  
  28024.  
  28025. Listing 3 spin.def: module definition file for spin control program
  28026. NAME SPIN
  28027. DESCRIPTION 'Demonstrates how to create & use spin controls'
  28028. EXETYPE WINDOWS
  28029. CODE PRELOAD MOVEABLE
  28030. DATA PRELOAD MOVEABLE MULTIPLE
  28031. HEAPSIZE 1024
  28032. STACKSIZE 5120
  28033.  
  28034.  
  28035.  
  28036.  
  28037.  
  28038.  
  28039.  
  28040.  
  28041.  
  28042.  
  28043.  
  28044.  
  28045.  
  28046.  
  28047.  
  28048.  
  28049.  
  28050.  
  28051.  
  28052.  
  28053.  
  28054.  
  28055.  
  28056.  
  28057.  
  28058.  
  28059.  
  28060.  
  28061.  
  28062.  
  28063.  
  28064.  
  28065.  
  28066.  
  28067.  
  28068.  
  28069. Scrolling List Dialog for Scientific Programming
  28070.  
  28071.  
  28072. Steve Welstead
  28073.  
  28074.  
  28075. This article is not available in electronic form.
  28076.  
  28077.  
  28078.  
  28079.  
  28080.  
  28081.  
  28082.  
  28083.  
  28084.  
  28085.  
  28086.  
  28087.  
  28088.  
  28089.  
  28090.  
  28091.  
  28092.  
  28093.  
  28094.  
  28095.  
  28096.  
  28097.  
  28098.  
  28099.  
  28100.  
  28101.  
  28102.  
  28103.  
  28104.  
  28105.  
  28106.  
  28107.  
  28108.  
  28109.  
  28110.  
  28111.  
  28112.  
  28113.  
  28114.  
  28115.  
  28116.  
  28117.  
  28118.  
  28119.  
  28120.  
  28121.  
  28122.  
  28123.  
  28124.  
  28125.  
  28126.  
  28127.  
  28128.  
  28129.  
  28130. An Alternative to Large Switch Statements
  28131.  
  28132.  
  28133. Matt Weisfeld
  28134.  
  28135.  
  28136. Matt Weisfeld currently works for the Allen-Bradley Company in Cleveland, Ohio
  28137. developing software for UNIX, VMX, DOS, Windows and other platforms. The
  28138. author has recently published a book, entitled Developing C Language Portable
  28139. System Call Libraries, which is available from the WILEY-QED group of John
  28140. Wiley & Sons. Matt can be reached via Compuserve [71620,2171] or Internet
  28141. [maw@ferrari.da.hh.ab.com].
  28142.  
  28143.  
  28144. After many years of writing structured C code, I find it difficult to adjust
  28145. to many aspects of Windows programming. The enormous size of many Windows
  28146. program switch statements makes me particularly uncomfortable. Given the
  28147. event-driven nature of Windows, I can understand why these statements grow so
  28148. large, but my instincts tell me to avoid creating such constructs as a general
  28149. practice. Just imagine a menubar/pulldown menu structure with 50 potential
  28150. choices generating 50 possible messages (the Borland C++ for Windows 3.1
  28151. compiler environment has over 60). If I create such a menu via the traditional
  28152. approach my resulting switch statement will contain 50 case statements. That
  28153. makes for pretty hard-to-read code. In this article I demonstrate how to
  28154. replace these gigantic switch statements with something more manageable.
  28155.  
  28156.  
  28157. An Alternative
  28158.  
  28159.  
  28160. As an alternative to the switch statement, I use a function table with a
  28161. look-up routine. This routine uses the parameter (formerly the switch
  28162. statement's control variable) as a search value. When the routine finds a key
  28163. in the table matching the search value, it executes the function associated
  28164. with the key.
  28165. I create the internal function table with the following prototype: 
  28166. typedef struct {
  28167. WPARAM message;
  28168. int (*funcptr)(HWND);
  28169. } INFUNCS;
  28170. The first field in the structure, defined as type WPARM, corresponds to the
  28171. argument of the same type obtained from main Windows message loop. The second
  28172. field is a function pointer to a routine. In this example, the parameter in
  28173. the internal function call, a window handle (HWND), indicates where to display
  28174. a messagebox. (The actual type and number of arguments passed in a real
  28175. application will be different, depending on the application.)
  28176. As an illustration, consider a Windows application, called internal, which
  28177. creates a single window (I use the BORLAND C++ Compiler v3.1; however, this
  28178. technique is not compiler dependent). This window has a simple menu structure
  28179. providing two choices on the menubar, FILE and EDIT. Selecting either of these
  28180. options produces a pulldown menu that contains the actions available to the
  28181. user (FILE has eight, EDIT has six). Figure 1 shows a sample window with the
  28182. EDIT pulldown menu activated. Choosing one of these pulldown items, using
  28183. either the mouse or the keyboard, sends a WM_COMMAND message to the program
  28184. message loop. After recognizing WM_COMMAND, the code checks the wParam
  28185. argument for the actual message type, which represents the menu choice.
  28186. After encountering a WM_COMMAND message, a simple table search attempts to
  28187. match the wParam value to its counterpart in the table. If found, the code
  28188. calls the corresponding internal function. If not found, the code generates a
  28189. messagebox displaying an internal error. This error should theoretically not
  28190. occur unless a legitimate wParam value is not in the table. (In this case, you
  28191. can simply add the missing value to the table.)
  28192. The code for all the internal functions resides in funcs.c (see Listing 7).
  28193. The routine for p_file_open looks like this (the others are almost identical):
  28194. int p_file_open(HWND hwnd)
  28195. {
  28196. MessageBox (hwnd, "file_open",
  28197. "COMMAND SELECTED", MB_OK);
  28198. return(0);
  28199. }
  28200. The initialization of the function table, called infuncs, is specified in the
  28201. file funcs.h (see Listing 5). An abbreviated example of the table definition
  28202. looks like this:
  28203. INFUNCS infuncs[] = {
  28204. {WM_FILE_NEW, p_file_new},
  28205. {WM_FILE_OPEN, p_file_open},
  28206.  
  28207. ...
  28208. {WM_EDIT_CLEAR, p_edit_clear},
  28209. {NULL, NULL},
  28210. };
  28211.  
  28212.  
  28213. Code Comparison
  28214.  
  28215.  
  28216. The complete code for the main procedure resides in the file internal.c (see
  28217. Listing 6). The code to perform the table search, and thus replace the switch
  28218. statement, is as follows:
  28219. case WM_COMMAND:
  28220. {
  28221. for (i=0; infuncs[i].message !=
  28222. NULL; i++) {
  28223. if (infuncs[i].message ==
  28224. wParam) {
  28225. status = (*in-
  28226. funcs[i].funcptr)(hwnd);
  28227. break;
  28228. }
  28229. }
  28230.  
  28231.  
  28232. if (infuncs[i].message == NULL) {
  28233. MessageBox (hwnd, "Bad Message",
  28234. "INTERNAL ERROR", MB_OK);
  28235. break;
  28236. }
  28237. }
  28238. The algorithm performs a sequential search that terminates when a message
  28239. matches an entry in the table (by a break statement) or when the search
  28240. reaches the NULL values (meaning the message was not in the table, an error as
  28241. described earlier). Compare this code to the switch statement in Listing 3.
  28242. Even though the switch statement contains only 14 case statements, the
  28243. function table still provides a code savings.
  28244.  
  28245.  
  28246. Performance Considerations
  28247.  
  28248.  
  28249. It is true that this technique introduces overhead in the form of table
  28250. searches and function calls. I was initially concerned that this approach
  28251. would degrade performance beyond the limits of acceptability. However,
  28252. discussion on the BORLAND forum of Compuserve provided a consensus that there
  28253. should be no performance problems relative to switch statements. I don't
  28254. expect performance to suffer compared to switch statements because the
  28255. compiler usually treats a large case statement as a table look-up anyway, and
  28256. function calls require very little overhead. However, there is no easy way of
  28257. predicting how this technique will impact a specific application.
  28258. You can also speed up table searches by using smarter algorithms (see the
  28259. sidebar, "Choosing a Table Search Algorithm"). My sample application just uses
  28260. a linear search -- one of the slowest when it comes to large tables.
  28261.  
  28262.  
  28263. Conclusion
  28264.  
  28265.  
  28266. Despite the added overhead, the compelling reason to abandon the switch
  28267. statement is to enhance readability. When using switch statements, more
  28268. messages require more case statements. When using internal functions, the
  28269. addition of a message requires no change to the message processing code --
  28270. only a new entry in the table and a new internal function. The utility gained
  28271. by using internal functions greatly increases as the number of messages
  28272. increases.
  28273. References:
  28274. Data Structures, Algorithms and Program Style using C, James F. Korsh and
  28275. Leonard J. Garrett. PWS-Kent Publishing Company. Boston, 1988. p 358.
  28276. Introduction to Data Structures with Pascal, Thomas Naps and Bhagat Singh.
  28277. West Publishing Company. St. Paul, 1986. p 317.
  28278. Peter Norton's Windows 3.1 Power Programming Techniques, 2nd ed., Peter Norton
  28279. and Paul Yao. Bantam Books, 1992.
  28280. Programming Windows, 2nd ed. Charles Petzold. Microsoft Press, 1990.
  28281. Choosing a Table Search Algorithm
  28282. Choosing the proper search algorithm can greatly impact the efficiency of a
  28283. program. This is especially true as a table grows very large. For example,
  28284. consider an array with n elements (size n, assume elements start at 1). Each
  28285. element of the array is a structure with the first field designated as the
  28286. key. The search algorithm will compare a search value to each key in the table
  28287. until it finds a match or the table is exhausted. I describe two types of
  28288. search techniques here: the linear search and the binary search. (Hash tables
  28289. are also an alternative to certain searching applications; however, they are
  28290. mainly used for tables that are highly volatile or built at run time. The
  28291. table in this article is fairly static and is built at compile time.)
  28292.  
  28293.  
  28294. Linear Search
  28295.  
  28296.  
  28297. Linear searches are the simpler of the two and normally considered the least
  28298. efficient. The search progresses in a linear fashion (i.e. table position 1,
  28299. position 2, ... position n). Processing time increases linearly with n. As a
  28300. measure of efficiency, I present figures in terms of number of accesses
  28301. required:
  28302. Best Case : 1 -- a search for the first element in the table
  28303. Worst Case : n -- a search for the last element in the table
  28304. Average Case : n/2 -- on average half the numbers are searched
  28305. A simple technique to eliminate a comparison step can increase the efficiency
  28306. of a linear search by 20 to 50 percent. Most unimproved linear searches will
  28307. require a simple comparison to limit the loop (ex: if i <= n) at each
  28308. iteration. An improved algorithm elimates these comparisons by first placing a
  28309. copy of the search value at location n+1. The algorithm can now walk through
  28310. the array without checking if it has exceeded maximum table size, since it is
  28311. certain stop its search at location n+1, if not before. If the search
  28312. algorithm progressed as far as location n+1, then the search value was not
  28313. originally in the table.
  28314. It is also possible to order the array so that the most frequently accessed
  28315. values are at the top of the table. However, this technique requires the
  28316. program to keep statistics as the table is continuously searched, and to
  28317. perform periodic re-ordering of the table.
  28318.  
  28319.  
  28320. Binary Search
  28321.  
  28322.  
  28323. Binary searches are in many cases more efficient than linear searches.
  28324. However, they have one major drawback: the table must be pre-sorted. Binary
  28325. searches use the bisection method to search the table, much like a human looks
  28326. through an alphabetized index. For example, if the table is of size 10, the
  28327. first element searched is at the mid-point (1+10)/2 = 5. If element 5 is not a
  28328. match, a simple comparison tells us whether the key is above or below element
  28329. 5 (since the table is sorted). At this point half the table is eliminated from
  28330. further consideration. If the value is greater than the key in element 5, then
  28331. the search is confined to locations 6 to 10. The new mid-point is (6+10)/2 =
  28332. 8. This bisection process continues until either a match is made or the last
  28333. element is searched. For a listing of the actual algorithm, consult the
  28334. references provided. In this example, searching the entire table takes at most
  28335. four iterations (compared to ten for the linear search). The access figures
  28336. provided here assume that the bisection splits the remaining table into two
  28337. parts that differ in size by at most one. The figures for binary searches are
  28338. as follows:
  28339. Best Case : 1 -- a search finds the first mid-point searched in
  28340.  the table
  28341. Worst Case : log2 n -- a search finds the last element searched in the
  28342.  table
  28343. Average Case : (log2 n)-1 -- if each element is searched with equal
  28344.  frequency
  28345.  
  28346.  
  28347. Comparing Linear and Binary Search
  28348.  
  28349.  
  28350. For large tables, the binary search requires much less time than the linear
  28351. search in the worst case. Consider a table of 50,000 items. The worst-case
  28352. scenario for a binary search requires no more than 16 accesses whereas the
  28353. worst scenario for the linear search takes 50,000 accesses! In situations
  28354. where the majority of searches are likely to fail, the efficiency of the
  28355. binary search is very compelling. However, the logic of the binary search is
  28356. much more complex (and thus much more time-consuming) than the logic of the
  28357. linear search. Also, as already mentioned, the requirement for a sorted table
  28358. is a drawback of the binary search. In this case, efficiency is impacted by
  28359. the sorting method employed. Thus, for certain applications with small tables,
  28360. the linear search may well be the faster of the two.
  28361. The volatility of the table also influences the choice of search technique. If
  28362. the table is constantly being updated, then the sorts required by the binary
  28363. search will occur with greater frequency, decreasing its efficiency relative
  28364. to the linear search. However, if the table is static, then the sort is
  28365. performed only once, thus increasing the attractiveness of the binary search.
  28366. In the context of the article, the size of the table is relatively small, so
  28367. it might be best to use a linear search. Even in larger Windows applications,
  28368. the number of case statements in a single switch structure will most certainly
  28369. be less than 1000. However, the internal function table is very static and
  28370. thus eliminates the primary objection to using a binary search.
  28371.  
  28372. Figure 1 A sample window with an activated pulldown menu
  28373.  
  28374. Listing 1 Definitions for internal application
  28375. /************************************************************
  28376. File : internal.h
  28377. Author : Matt Weisfeld
  28378. ************************************************************/
  28379. #define WM_FILE-NEW 100
  28380. #define WM_FILE_OPEN 101
  28381. #define WM_FILE_SAVE 102
  28382. #define WM_FILE_SAVE_AS 103
  28383. #define WM_FILE_SAVE_ALL 104
  28384. #define WM_FILE_PRINT 105
  28385. #define WM_FILE_PRINTER_SETUP 106
  28386. #define WM_FILE_EXIT 107
  28387. #define WM_EDIT_UNDO 108
  28388. #define WM_EDIT_REDO 109
  28389. #define WM_EDIT_CUT 110
  28390. #define WM_EDIT_COPY 111
  28391. #define WM_EDIT_PASTE 112
  28392. #define WM_EDIT_CLEAR 113
  28393.  
  28394. /* End of File */
  28395.  
  28396.  
  28397. Listing 2 Resource file for internal
  28398. /************************************************************
  28399. File : internal.rc
  28400. Author : Matt Weisfeld
  28401. ************************************************************/
  28402. #include "internal.h"
  28403.  
  28404. 1 MENU
  28405. {
  28406. POPUP "&File"
  28407. {
  28408. MENUITEM "&New", WM_FILE_NEW
  28409. MENUITEM "&Open...", WM_FILE_OPEN
  28410. MENUITEM "&Save", WM_FILE_SAVE
  28411. MENUITEM "Save As...", WM_FILE_SAVE_AS
  28412. MENUITEM "Save &All...", WM_FILE_SAVE_ALL
  28413. MENUITEM SEPARATOR
  28414. MENUITEM "&Print", WM_FILE_PRINT
  28415. MENUITEM "P&rinter setup...", WM_FILE_PRINTER_SETUP
  28416. MENUITEM SEPARATOR
  28417. MENUITEM "E&xit", WM_FILE_EXIT
  28418. }
  28419. POPUP "&Edit"
  28420. {
  28421. MENUITEM "&Undo", WM_EDIT_UNDO
  28422. MENUITEM "&Redo", WM_EDIT_REDO
  28423. MENUITEM SEPARATOR
  28424. MENUITEM "Cu&t", WM_EDIT_CUT
  28425. MENUITEM "&Copy", WM_EDIT_COPY
  28426. MENUITEM "&Paste", WM_EDIT_PASTE
  28427. MENUITEM "Cl&ear", WM_EDIT_CLEAR
  28428. }
  28429. }
  28430.  
  28431.  
  28432. /* End of File */
  28433.  
  28434.  
  28435. Listing 3 Example of an unwieldy switch statement
  28436. /**************************************************************
  28437. Switch statement example
  28438. **************************************************************/
  28439. case WM_COMMAND:
  28440. {
  28441. switch (wParam) {
  28442. case WM_FILE_NEW:
  28443. MessageBox (hwnd, "file_new", "COMMAND SELECTED", MB_OK);
  28444. break;
  28445. case WM_FILE_OPEN:
  28446. MessageBox (hwnd, "file_open", "COMMAND SELECTED", MB_OK);
  28447. break;
  28448. case WM_FILE_SAVE:
  28449. MessageBox (hwnd, "file_save", "COMMAND SELECTED", MB_OK);
  28450. break;
  28451. case WM_FILE_SAVE_AS:
  28452. MessageBox (hwnd, "file_save_as", "COMMAND SELECTED", MB_OK);
  28453. break;
  28454. case WM_FILE_SAVE_ALL:
  28455. MessageBox (hwnd, "file_save_all", "COMMAND SELECTED", MB_OK);
  28456. break;
  28457. case WM_FILE_PRINT:
  28458. MessageBox (hwnd, "file_print", "COMMAND SELECTED", MB_OK);
  28459. break;
  28460. case WM_FILE_PRINTER_SETUP:
  28461. MessageBox (hwnd, "file_printer_setup", "COMMAND SELECTED", MB_OK);
  28462. break;
  28463. case WM_FILE_EXIT:
  28464. MessageBox (hwnd, "file_exit", "COMMAND SELECTED", MB_OK);
  28465. break;
  28466. case WM_EDIT_UNDO:
  28467. MessageBox (hwnd, "edit_undo", "COMMAND SELECTED", MB_OK);
  28468. break;
  28469. case WM_EDIT_REDO:
  28470. MessageBox (hwnd, "edit_redo", "COMMAND SELECTED", MB_OK);
  28471. break;
  28472. case WM_EDIT_CUT:
  28473. MessageBox (hwnd, "edit_cut", "COMMAND SELECTED", MB_OK);
  28474. break;
  28475. case WM_EDIT_COPY:
  28476. MessageBox (hwnd, "edit_copy", "COMMAND SELECTED", MB_OK);
  28477. break;
  28478. case WM_EDIT_PASTE:
  28479. MessageBox (hwnd, "edit_paste", "COMMAND SELECTED", MB_OK);
  28480. break;
  28481. case WM_EDIT_CLEAR:
  28482. MessageBox (hwnd, "edit_clear", "COMMAND SELECTED", MB_OK);
  28483. break;
  28484. }
  28485. }
  28486.  
  28487. /* End of File */
  28488.  
  28489.  
  28490. Listing 4 Prototypes for internal functions example
  28491.  
  28492. /************************************************************
  28493.  
  28494. File : proto.h
  28495.  
  28496. Author : Matt Weisfeld
  28497.  
  28498. ************************************************************/
  28499. typedef struct {
  28500. WPARAM message;
  28501. int (*funcptr)(HWND);
  28502. } INFUNCS;
  28503.  
  28504. LRESULT CALLBACK InternalWndProc (HWND, UINT, WPARAM, LPARAM);
  28505.  
  28506. int p_file_new();
  28507. int p_file_open();
  28508. int p_file_save();
  28509. int p_file_save_as();
  28510. int p_file_save_all();
  28511. int p_file_print();
  28512. int p_file_printer_setup();
  28513. int p_file_exit();
  28514. int p_edit_undo();
  28515. int p_edit_redo();
  28516. int p_edit_cut();
  28517. int p_edit_copy();
  28518. int p_edit_paste();
  28519. int p_edit_clear();
  28520.  
  28521. /* End of File */
  28522.  
  28523.  
  28524. Listing 5 Map table to internal function
  28525. /************************************************************
  28526.  
  28527. File : funcs.h
  28528.  
  28529. Author : Matt Weisfeld
  28530.  
  28531. ***********************************************************/
  28532. INFUNCS infuncs[] = {
  28533. {WM_FILE_NEW, p_file_new},
  28534. {WM_FILE_OPEN, p_file_open},
  28535. {WM_FILE_SAVE, p_file_save},
  28536. {WM_FILE_SAVE_AS, p_file_save_as},
  28537. {WM_FILE_SAVE_ALL, p_file_save_all},
  28538. {WM_FILE_PRINT, p_file_print},
  28539. {WM_FILE_PRINTER_SETUP, p_file_printer_setup},
  28540. {WM_FILE_EXIT, p_file_exit},
  28541. {WM_EDIT_UNDO, p_edit_undo},
  28542. {WM_EDIT_REDO, p_edit_redo},
  28543. {WM_EDIT_CUT, p_edit_cut},
  28544. {WM_EDIT_COPY, p_edit_copy},
  28545. {WM_EDIT_PASTE, p_edit_paste},
  28546. {WM_EDIT_CLEAR, p_edit_clear},
  28547. {NULL,NULL},
  28548. }
  28549.  
  28550. /* End of File */
  28551.  
  28552.  
  28553.  
  28554. Listing 6 Demonstrates use of internal functions instead of a switch
  28555. statement.
  28556. /************************************************************
  28557. File : internal.c
  28558. Author : Matt Weisfeld
  28559. ************************************************************/
  28560.  
  28561. #define STRICT
  28562. #include <Windows.H>
  28563. #include "proto.h"
  28564. #include "internal.h"
  28565. #include "funcs.h"
  28566.  
  28567. /* global variables */
  28568.  
  28569. char achWndClass[] = "Internal:MAIN";
  28570. char achAppName[] = "Menu using Internal Functions";
  28571.  
  28572. /* Main Function */
  28573.  
  28574. int PASCAL WinMain (HINSTANCE hInstance,
  28575. HINSTANCE hPrevInstance, LPSTR lpszCmdLine,
  28576. int cmdShow) {
  28577. HWND hwnd;
  28578. MSG msg;
  28579. WNDCLASS wndclass;
  28580.  
  28581. if (!hPrevInstance)
  28582. {
  28583. wndclass.lpszClassName = achWndClass;
  28584. wndclass.hInstance = hInstance;
  28585. wndclass.lpfnWndProc = InternalWndProc;
  28586. wndclass.hCursor = NULL;
  28587. wndclass.hIcon = NULL;
  28588. wndclass.lpszMenuName = "#1";
  28589. wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  28590. wndclass.style = NULL;
  28591. wndclass.cbClsExtra = 0;
  28592. wndclass.cbWndExtra = 0;
  28593.  
  28594. RegisterClass( &wndclass);
  28595. }
  28596.  
  28597. hwnd = CreateWindowEx(0L, achWndClass, achAppName,
  28598. WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT,
  28599. 0, NULL, NULL, hInstance, NULL);
  28600.  
  28601. ShowWindow (hwnd, cmdShow);
  28602.  
  28603. while (GetMessage(&msg, 0, 0, 0))
  28604. {
  28605. TranslateMessage(&msg);
  28606. DispatchMessage(&msg);
  28607. }
  28608. return 0;
  28609. }
  28610.  
  28611. /* Process messages */
  28612.  
  28613.  
  28614. LRESULT CALLBACK InternalWndProc (HWND hwnd, UINT mMsg,
  28615. WPARAM wParam, LPARAM lParam)
  28616. {
  28617. int i, status;
  28618.  
  28619. switch (mMsg)
  28620. {
  28621. case WM_COMMAND:
  28622. {
  28623. /* Loop through internal function table */
  28624. for (i=0; infuncs[i].message != NULL; i++) {
  28625. // If message found, execute function
  28626. if (infuncs[i].message == wParam) {
  28627. status = (*infuncs[i].funcptr)(hwnd);
  28628. break;
  28629. }
  28630. }
  28631.  
  28632. /* If message does not exists, internal error */
  28633. if (infuncs[i].message == NULL) {
  28634. MessageBox (hwnd, "Bad Message",
  28635. "INTERNAL ERROR", MB_OK);
  28636. break;
  28637. }
  28638.  
  28639. /* If file_exit is called, exit application */
  28640. if (wParam == WM_FILE_EXIT)
  28641. {
  28642. SendMessage (hwnd, WM_SYSCOMMAND, SC_CLOSE. 0L);
  28643. }
  28644. }
  28645. break; /* WM_COMMAND */
  28646.  
  28647. case WM_DESTROY:
  28648. PostQuitMessage(0);
  28649. break;
  28650.  
  28651. default:
  28652. return(DefWindowProc(hwnd,mMsg,wParam,lParam));
  28653. break;
  28654. }
  28655. return 0L;
  28656. }
  28657.  
  28658. /* End of File */
  28659.  
  28660.  
  28661. Listing 7 Internal functions to replace switch statement.
  28662. /************************************************************
  28663. File : funcs.c
  28664. Author : Matt Weisfeld
  28665. ************************************************************/
  28666. #include <Windows.h>
  28667. #include "proto.h"
  28668.  
  28669. int p_file_new(HWND hwnd)
  28670. {
  28671. MessageBox (hwnd, "file_new", "COMMAND SELECTED", MB_OK);
  28672.  
  28673. return(0);
  28674. }
  28675. int p_file_open(HWND hwnd)
  28676. {
  28677. MessageBox (hwnd, "file_open", "COMMAND SELECTED", MB_OK);
  28678. return(0);
  28679. }
  28680. int p_file_save(HWND hwnd)
  28681. {
  28682. MessageBox (hwnd, "file_save", "COMMAND SELECTED", MB_OK);
  28683. return(0);
  28684. }
  28685. int p_file_save_as(HWND hwnd)
  28686. {
  28687. MessageBox (hwnd, "file_save_as", "COMMAND SELECTED", MB_OK);
  28688. return(0);
  28689. }
  28690. int p_file_save_all(HWND hwnd)
  28691. {
  28692. MessageBox (hwnd, "file_save_all", "COMMAND SELECTED", MB_OK);
  28693. return(0);
  28694. }
  28695. int p_file_print(HWND hwnd)
  28696. {
  28697. MessageBox (hwnd, "file_print", "COMMAND SELECTED", MB_OK);
  28698. return(0);
  28699. }
  28700. int p_file_printer_setup(HWND hwnd)
  28701. {
  28702. MessageBox (hwnd, "file_printer_setup", "COMMAND SELECTED",
  28703. MB_OK);
  28704. return(0);
  28705. }
  28706. int p_file_exit(HWND hwnd)
  28707. {
  28708. MessageBox (hwnd, "file_exit", "COMMAND SELECTED", MB_OK);
  28709. return(0);
  28710. }
  28711. int p_edit_undo(HWND hwnd)
  28712. {
  28713. MessageBox (hwnd, "edit_undo", "COMMAND SELECTED", MB_OK);
  28714. return(0);
  28715. }
  28716. int p_edit_redo(HWND hwnd)
  28717. {
  28718. MessageBox (hwnd, "edit_redo", "COMMAND SELECTED", MB_OK);
  28719. return(0);
  28720. }
  28721. int p_edit_cut(HWND hwnd)
  28722. {
  28723. MessageBox (hwnd, "edit_cut", "COMMAND SELECTED", MB_OK);
  28724. return(0);
  28725. }
  28726. int p_edit_copy(HWND hwnd)
  28727. {
  28728. MessageBox (hwnd, "edit_copy", "COMMAND SELECTED", MB_OK);
  28729. return(0);
  28730. }
  28731. int p_edit_paste(HWND hwnd)
  28732.  
  28733. {
  28734. MessageBox (hwnd, "edit_paste", "COMMAND SELECTED", MB_OK);
  28735. return(0);
  28736. }
  28737. int p_edit_clear(HWND hwnd)
  28738. {
  28739. MessageBox (hwnd, "edit_clear", "COMMAND SELECTED", MB_OK);
  28740. return(0);
  28741. }
  28742.  
  28743. /* End of File */
  28744.  
  28745.  
  28746.  
  28747.  
  28748.  
  28749.  
  28750.  
  28751.  
  28752.  
  28753.  
  28754.  
  28755.  
  28756.  
  28757.  
  28758.  
  28759.  
  28760.  
  28761.  
  28762.  
  28763.  
  28764.  
  28765.  
  28766.  
  28767.  
  28768.  
  28769.  
  28770.  
  28771.  
  28772.  
  28773.  
  28774.  
  28775.  
  28776.  
  28777.  
  28778.  
  28779.  
  28780.  
  28781.  
  28782.  
  28783.  
  28784.  
  28785.  
  28786.  
  28787.  
  28788.  
  28789.  
  28790.  
  28791.  
  28792.  
  28793.  
  28794.  
  28795.  
  28796. Approximate String Matching
  28797.  
  28798.  
  28799. Thomas Phillips
  28800.  
  28801.  
  28802. Thomas Phillips, CCP, is a member of the ACM, IEEE:CS, ACL, SIAM, and AMTA.
  28803. His interests include linguistics, computational linguistics, parallel
  28804. processing, and cognitive science. He can be reached via e-mail at
  28805. 72713.2100@compuserve.com.
  28806.  
  28807.  
  28808.  
  28809.  
  28810. Introduction
  28811.  
  28812.  
  28813. People aren't perfect. Unfortunately, computers often demand perfection,
  28814. sometimes unjustly. One example is when the user must type in a word or a
  28815. string of words to tell a program what to do. Suppose you have a database
  28816. filled with names and addresses; you can write a program that allows a user to
  28817. search by name, but if you're not careful the program will be too selective.
  28818. Let's say the last name is Xavier, but the user thinks the name is spelled
  28819. Zavier. If your program uses a simple strcmp, it won't find the name. What you
  28820. need is a function to perform approximate matches. That's what freq_match does
  28821. (see Listing 1) -- it finds the closest match based on frequency
  28822. distributions.
  28823.  
  28824.  
  28825. Matching via Frequency Distributions
  28826.  
  28827.  
  28828. The frequency distribution of a word shows how many times each letter occurs.
  28829. For example, the frequency distribution of the word "LETTER" would be: L-1,
  28830. E-2, T-2, R-1. If you find the frequency distributions of two different words,
  28831. you can compare them for similarity in spelling.
  28832. The function freq_match uses the array freq_count[] to compare the
  28833. distributions of two words. Each element in freq_count[] holds the
  28834. distribution for its respective character. For example, the ASCII value for an
  28835. 'a' is 97 (or 61H). If a word being analyzed contained three a's (like
  28836. aardvark), then freq_count[97] would equal 3. (Note that freq_count[0]
  28837. shouldn't really be used; you would only expect one NULL in a string.)
  28838.  
  28839.  
  28840. Comparing Two Distributions
  28841.  
  28842.  
  28843. Once freq_count[] is loaded with the distribution for one word (what I call
  28844. the mask word), it's simple to calculate a number that tells how closely the
  28845. mask word matches another word (the test word). I call this number the
  28846. divergence. To start, let freq_count[] hold the frequency distribution of the
  28847. mask word. Next, subtract from it the frequency distribution of the test word.
  28848. Take the words "aardvark" and "apple," for example. The mask word is
  28849. "aardvark," so
  28850. freq_count['a'] is 3,
  28851. freq_count['r'] is 2,
  28852. freq_count['d'] is 1,
  28853. freq_count['v'] is 1,
  28854. freq_count['k'] is 1,
  28855. and all others are 0.
  28856. After the first letter of "apple" is processed, freq_count['a'] equals 2. This
  28857. is because my algorithm subtracts while it processes the test word. After all
  28858. the letters of the test word have been processed, freq_count[] will be as
  28859. follows:
  28860. freq_count['a'] is 2,
  28861. freq_count['r'] is 2,
  28862. freq_count['d'] is 1,
  28863. freq_count['v'] is 1,
  28864. freq_count['k'] is 1,
  28865. freq_count['p'] is -2,
  28866. freq_count['l'] is -1,
  28867. and freq_count['e'] is -1.
  28868. Finally, the algorithm adds up the absolute values of each of freq_count's
  28869. elements. The elements not listed (such as freq_count['q']) are assumed to
  28870. contain zeros. The total, or divergence, is 11. This huge divergence indicates
  28871. that "aardvark" and "apple" are quite disimilar. However, had I compared
  28872. "aardvark" with its misspelling, "ardvark," I would have arrived at a
  28873. divergence of 1, indicating extreme similarity.
  28874.  
  28875.  
  28876. Using freq_match
  28877.  
  28878.  
  28879. Using the freq_match function requires two steps. First, the program must
  28880. initialize the distribution array by passing it a valid mask word and an empty
  28881. string for the test word. An example would be:
  28882. freq_match("aardvark", "");
  28883. The second parameter must be an empty string -- find_match uses this empty
  28884. string as a switch enabling initialization. Since freq_match stores the mask
  28885. word's frequency distribution in a static array (freq_count[]), it only needs
  28886. to load each unique mask word once. In subsequent calls to freq_match, the
  28887. first parameter is ignored as long as the second parameter is not an empty
  28888. string. The second parameter should contain a valid test word. For example,
  28889. once "aardvark" has been loaded, you can test its divergence from "apple" by
  28890. using this statement:
  28891. divergence = freq_match("", "apple");
  28892. The return value will be 11.
  28893. To use freq_match, call it for each word in your list of test words. Keep
  28894. track of the minimum divergence and its corresponding test word. After you've
  28895. gone through the entire list of words (or optionally stopping when you find a
  28896. divergence of zero), accept the test word with the minimum divergence as the
  28897. most similar word.
  28898.  
  28899.  
  28900.  
  28901. Anagrams and Other Problems
  28902.  
  28903.  
  28904. Frequency distributions are great for most approximate matches, unfortunately,
  28905. they can't discriminate between a word and one of its anagrams. For example,
  28906. "TAB" is an anagram for "BAT." The frequency distribution for either of these
  28907. words is: T-1, A-1, B-1. One possible solution to this problem is to test the
  28908. first or last letters of the mask and test words. If one or both match, then
  28909. reduce the divergence by some constant number. If they don't match, increase
  28910. the divergence. For example:
  28911. divergence+=(mask_word[0]==test_word[0]) ?
  28912. ((divergence>0) ? -1 : = 0) : 1;
  28913. This strategy will slow down the matching process, though. In addition, it can
  28914. introduce some of its own problems (like matching "BIG" with "BOG").
  28915. Should you throw away strcmp and use freq_match for all of your string
  28916. matching needs? Obviously no, but there are places where freq_match() is very
  28917. useful. freq_match works best when a mask word must be found in a relatively
  28918. small list of test words (a few hundred or less). Typically, you would use
  28919. this function in applications where human errors are also frequent -- and that
  28920. covers a lot of ground.
  28921.  
  28922. Listing 1 Definition of function freq_match and test program
  28923. #include <stdio.h>
  28924.  
  28925. int freq_match(mask, test)
  28926. char *mask;
  28927. char *test;
  28928. {
  28929. static int freq_count[256]; /* the frequency */
  28930. /* distribution */
  28931. int divergence;
  28932. int i;
  28933.  
  28934. /* freq_match("maskword", ""); */
  28935. if (test[0] == '\0') {
  28936. /* initialize the distribution array */
  28937. for (i=0; i<256; i++)
  28938. freq_count[i++] = 0;
  28939.  
  28940. /* compute the distribution */
  28941. for (i=0; mask[i] != '\0'; i++)
  28942. freq_count[mask[i]] += 1;
  28943.  
  28944. /* return a zero for initialization */
  28945. return 0;
  28946. } /* if */
  28947.  
  28948. /* freq_match("don't care", "testword"); */
  28949. else {
  28950. /* subtract the freq. dist. of the test word */
  28951. for (i=0; test[i]!='\0'; i++)
  28952. freq_count[test[i]] -= 1;
  28953.  
  28954. /* compute the divergence */
  28955. for (divergence=0, i=0; i<256; i++)
  28956. divergence += abs(freq_count[i]);
  28957.  
  28958. /* this code is to reset the freq. dist. */
  28959. /* back to the settings for the mask word */
  28960. for (i=0; test[i]!='\0'; i++)
  28961. freq_count[test[i]] += 1;
  28962.  
  28963. return divergence;
  28964. } /* else */
  28965. } /* freq_match() */
  28966.  
  28967. void main()
  28968. {
  28969. char mask[80], test[80];
  28970.  
  28971. printf("Mask:");
  28972.  
  28973. gets(mask);
  28974.  
  28975. printf("Test:");
  28976. gets(test);
  28977.  
  28978. freq_match(mask, "");
  28979. printf("The divergence is %d.\n",
  28980. freq_match("", test));
  28981. } /* main() */
  28982.  
  28983. /* End of File */
  28984.  
  28985.  
  28986.  
  28987.  
  28988.  
  28989.  
  28990.  
  28991.  
  28992.  
  28993.  
  28994.  
  28995.  
  28996.  
  28997.  
  28998.  
  28999.  
  29000.  
  29001.  
  29002.  
  29003.  
  29004.  
  29005.  
  29006.  
  29007.  
  29008.  
  29009.  
  29010.  
  29011.  
  29012.  
  29013.  
  29014.  
  29015.  
  29016.  
  29017.  
  29018.  
  29019.  
  29020.  
  29021.  
  29022.  
  29023.  
  29024.  
  29025.  
  29026.  
  29027.  
  29028.  
  29029.  
  29030.  
  29031.  
  29032.  
  29033.  
  29034.  
  29035.  
  29036. Record-Oriented Data Compression
  29037.  
  29038.  
  29039. John W. Ross
  29040.  
  29041.  
  29042. John W. Ross is a computational scientist in the University of Toronto's High
  29043. Performance and Research Computing group. His interests include scientific
  29044. programming on highly parallel and vector supercomputers. John can be reached
  29045. at yjohn@utirc.utoronto.ca.
  29046.  
  29047.  
  29048.  
  29049.  
  29050. Introduction
  29051.  
  29052.  
  29053. Data compression programs are very useful but many of them have an annoying
  29054. shortcoming; they work on a file in toto, compressing the whole file in one
  29055. gulp. This shortcoming becomes especially significant when you are building a
  29056. data-base program or file manager. You can use a data compressor to compress
  29057. the resulting file, but once you do you no longer know where the record
  29058. boundaries are. You have to uncompress the file to extract or insert records,
  29059. so you lose some of the advantages of compression. A lot of similar
  29060. applications can't take advantage of conventional data compressors for this
  29061. reason.
  29062. We need a data compressor/decompressor that operates on the individual records
  29063. of a file. Such a program would allow you to compress a record, hand it to the
  29064. data base manager for storage, then later retrieve it and uncompress it. In
  29065. this article I present a program that does just that.
  29066.  
  29067.  
  29068. Implementation
  29069.  
  29070.  
  29071. I've chosen Huffman encoding as the algorithm to compress/decompress data
  29072. records (see the sidebar, "Two Popular Compression Algorithms," for a detailed
  29073. discussion of my choice).
  29074. Since Huffman encoding relies on character frequencies, it works with
  29075. practically any size data record. Of course, it is necessary to perform an
  29076. initial pass over the entire data set (or at least, a representative data set)
  29077. to determine the character frequencies. From that point on, as long as a data
  29078. record has the same character frequency characteristics as this data set, the
  29079. program will achieve good compression ratios.
  29080. I have implemented my compression scheme as three distinct entities. The first
  29081. is a stand-alone program that reads a file and generates a Huffman encoding
  29082. tree, which it then writes out to a file. The next is a function which
  29083. compresses a record using the encoding tree. The function takes a pointer to a
  29084. record to be compressed, and passes back a pointer to the compressed record.
  29085. The third entity is a function which decompresses a record. I have also
  29086. written a simple file management program to illustrate the application of
  29087. these functions. I consider each of the units in turn.
  29088.  
  29089.  
  29090. Building the Encoding Tree
  29091.  
  29092.  
  29093. Before compressing data records a program must build a Huffman encoding tree
  29094. from a sample of text similar to that which will be compressed. The encoding
  29095. tree will be based on the character frequencies found in the file. Program
  29096. bldtree (Listing 2) performs this operation.
  29097. hufftree.h (Listing 1) is an include file that defines the encoding tree
  29098. structure. By defining it in terms of short integers I am able to keep the
  29099. external storage for the tree to a minimum.
  29100. bldtree takes a single command-line argument, the name of the file to be
  29101. analyzed. The first thing bldtree does is determine a frequency count of the
  29102. characters in the file. Note that all characters are assigned a minimum count
  29103. of 1, so that the compression routine will be able to generate a code for any
  29104. character, even if it didn't appear in the file. The remainder of the program
  29105. builds the encoding tree from the table of character frequencies. bldtree
  29106. creates a file called htree.dat and writes the encoding tree to the file.
  29107.  
  29108.  
  29109. The Compression Function
  29110.  
  29111.  
  29112. The function compress and its ancillary functions, encode and emit, are shown
  29113. in Listing 3. compress takes a pointer to a character buffer (containing the
  29114. uncompressed data record) and the buffer length as arguments, and returns a
  29115. pointer to another character buffer containing the compressed data record.
  29116. Note that the first thing compress does is put the length of the input data
  29117. record into the first byte of the output record. compress stores this length
  29118. in the first byte so the decompression function will know how many bytes it
  29119. has to decode from the compressed record. Using one byte to store the length
  29120. restricts the maximum input record length to 255 bytes, minus one byte to
  29121. store that length -- so the input record must contain at most 254 bytes of
  29122. data. If you wanted to use larger records you could store the length as a
  29123. short integer using the first two bytes of the output record. (This assumes
  29124. that the input records are variable length. If they are fixed length, and the
  29125. application knows that length then it is unnecessary to store the length in
  29126. the record.)
  29127. For each character in the input buffer, compress calls function encode, which
  29128. in turn uses the global struct ht, the encoding tree. The application program
  29129. is responsible for defining storage for ht, and either reading it from an
  29130. external file or constructing it. The final call to emit in compress pads out
  29131. the final byte of the compressed record if necessary.
  29132.  
  29133.  
  29134. Decompression
  29135.  
  29136.  
  29137. To invert the compression process, an application program passes a compressed
  29138. record to the function decode, (Listing 4) which expands the record and
  29139. returns it in its original form along with its length in a separate buffer.
  29140. (It should be obvious, but in case it isn't I want to stress that the encoding
  29141. tree used to decompress the data records must be the same one that was used to
  29142. compress them.)
  29143.  
  29144.  
  29145. An Example
  29146.  
  29147.  
  29148. The program called filer (Listing 5) shows how the compression routines may be
  29149. applied. filer takes two command-line arguments: the name of an input file to
  29150. be compressed and the name of an output file that will contain the compressed
  29151. records. filer is designed to work with a plain text file. filer treats each
  29152. line in the file, delineated by a newline (\n), as a record to be compressed.
  29153. filer also writes an index file as it processes the text file so that
  29154. individual compressed records may be retrieved later.
  29155. filer assumes that a file called htree.dat containing the Huffman encoding
  29156. tree exists.
  29157. filer keeps track of the number of bytes in the input and output records and
  29158. prints out the average compression ratio.
  29159. unfiler (Listing 6) illustrates the application of decompression. unfiler can
  29160. read any record from the compressed file by number, but it's set up in this
  29161. example to read all of the records in sequence.
  29162. To use these programs with a text file called misc. txt, you would type the
  29163. following commands:
  29164. bldtree misc.txt
  29165.  
  29166. filer misc.txt misc.cmp
  29167. unfiler misc.cmp misc.unc
  29168. misc.cmp is the file containing the compressed records and misc.unc contains
  29169. the decompressed versions of the records. misc.txt and misc.unc should be
  29170. identical. These programs will compile and run under MS-DOS and most versions
  29171. of UNIX.
  29172. To work with a file that does not consist of individual lines of text, it is
  29173. simple to modify filer to read blocks of text or data rather than lines. Just
  29174. replace the program lines that read:
  29175. while (fgets(bufin, MAXBUF-1, in) != NULL)
  29176. { inlen = strlen(bufin);
  29177. ncharin += inlen;
  29178. with
  29179. while ((inlen = fread(bufin, 1, 255, in)) > 0)
  29180. { ncharin += inlen;
  29181. This modification will yield slightly better compression ratios. You don't
  29182. have to modify unfiler to recover the output file.
  29183.  
  29184.  
  29185. Simple Encryption
  29186.  
  29187.  
  29188. This compression process also performs encryption as a side effect. The
  29189. encryption key is the particular Huffman encoding tree in use when the data
  29190. was compressed. Someone looking to decrypt your data would have to determine
  29191. that it had been compressed using Huffman encoding and then would have to
  29192. determine what sort of character frequency distribution had been used. While
  29193. this kind of challenge probably wouldn't stop a professional code breaker, it
  29194. might dissuade the casual snooper.
  29195.  
  29196.  
  29197. Summary
  29198.  
  29199.  
  29200. In this article I have shown how to adapt a common file compression algorithm,
  29201. Huffman encoding, so that it may be applied to the individual data records in
  29202. a file. This technique would be useful in database or file management
  29203. applications in which parts of the file have to be stored or retrieved
  29204. independently. The compression process also also serves as a means of
  29205. encrypting the data.
  29206. Two Popular Compression Algorithms
  29207. Two general purpose data compression algorithms are currently in widespread
  29208. use. These are the Lempel-Ziv-Welch (LZW) algorithm (Welch, Terry A., "A
  29209. Technique for High Performance Data Compression," IEEE Computer, June, 1984.)
  29210. and the Huffman encoding algorithm. Both are lossless compression methods, but
  29211. rely on completely different compression mechanisms.
  29212. The LZW algorithm generally yields better results and does not require
  29213. external tables. This algorithm replaces strings of characters with single
  29214. codes which create a sort of expanded alphabet. LZW adds new strings to a
  29215. string table, which is created dynamically as the input data stream is
  29216. processed. As output LZW produces the codes representing strings in the input
  29217. stream.
  29218. In the decompression phase the LZW algorithm reads the string codes, from
  29219. which it can dynamically reconstruct the string table and output the original
  29220. strings of characters.
  29221. The Huffman encoding algorithm is based on the observation that certain
  29222. characters, especially in text, occur more frequently than others. Instead of
  29223. allocating an 8-bit ASCII code to each character the Huffman algorithm uses
  29224. variable-length codes for each character. Frequently occurring characters are
  29225. represented by codes with fewer bits while rarely occurring characters are
  29226. represented by codes with more bits.
  29227. Static Huffman encoding requires a table of probabilities of occurrence of
  29228. each character before compression. Programs may derive this table from a
  29229. pre-scan of the actual data if it is available, or from statistical
  29230. observations of similar data -- this works well for English text, for example.
  29231. In any event, the program constructs an encoding tree from the probability
  29232. information. This tree must be available in both the compression and
  29233. decompression phases. Though the output consists of variable-length bit
  29234. strings for each character, no end-of-character markers are required -- the
  29235. algorithm "knows" when it has reached the end of a code in the decompression
  29236. phase.
  29237.  
  29238.  
  29239. Choosing an Algorithm for Compression
  29240.  
  29241.  
  29242. As mentioned above, the LZW algorithm generally produces better compression
  29243. ratios but it is not suitable for record-oriented compression. The records
  29244. involved may be quite short and may contain no repeated strings. Since the
  29245. records may be processed independently (for example, inserting one record at
  29246. random into a data base) LZW can't take advantage of the fact that the data
  29247. base or file as a whole may have much repeated information in it. Since the
  29248. LZW algorithm works by replacing repeated strings with shorter codes it would
  29249. often provide unsatisfactory compression in this application.
  29250. Huffman encoding will work with individual records, but it's not without
  29251. drawbacks. First, Huffman encoding requires an encoding tree be available
  29252. during both compression and decompression phases. If you use an encoding tree
  29253. that was constructed from data with a particular character frequency
  29254. distribution to encode data that has a different character frequency
  29255. distribution, your results will not be satisfactory. Second, the algorithm
  29256. doesn't work well for data containing characters that occur with a roughly
  29257. uniform frequency. Binary data is particularly bad -- files such as a program
  29258. executable don't compress -- they will probably increase in size!
  29259. A third problem stems from the algorithm's conversion of input data as a
  29260. string of bytes to output data as a string of bits. Since most computers can
  29261. only address data units down to the byte level, this output string must be
  29262. padded out to an integral number of bytes. Unfortunately, in the decompression
  29263. phase the Huffman algorithm can't tell whether these extra few bits represent
  29264. more characters or whether they are just padding. It is therefore necessary to
  29265. inform the decompression program what the original record length was so it
  29266. will know when to stop.
  29267. Having said all that, the algorithm generally works well for text data. For
  29268. English text you can expect data to be compressed to about 60% of its original
  29269. size. The algorithm also works well in situations where your data consists of
  29270. numbers represented by ASCII digits -- in this case you can get compressions
  29271. closer to 40% of the original size.
  29272.  
  29273. Listing 1 hufftree.h: defines Huffman encoding tree structure
  29274. struct htree
  29275. { short parent;
  29276. short right;
  29277. short left;
  29278. };
  29279.  
  29280. #ifdef MAIN
  29281. struct htree ht[512];
  29282. short root;
  29283. #else
  29284. extern struct htree ht[];
  29285. extern short root;
  29286. #endif
  29287.  
  29288. void compress(int inlen, char *bufin, int *outlen, char
  29289. *bufout);
  29290. void encode(short h, short child, int *outlen, char *bufout);
  29291. void emit(int bit, int *outlen, char *bufout);
  29292. void decode(char *bufin, int *outlen, char *bufout);
  29293.  
  29294.  
  29295. /* End of File */
  29296.  
  29297.  
  29298. Listing 2 A program that reads a file and generates a Huffman encoding tree
  29299. based on the character frequencies in that file.
  29300. /* ---------------------- bldtree - main --------------------
  29301. */
  29302.  
  29303. #include <stdio.h>
  29304. #define MAIN
  29305. #include "hufftree.h"
  29306.  
  29307. long htcnt[512];
  29308.  
  29309. main(int argc, char *argv[])
  29310. {
  29311. FILE *fin, *fout;
  29312. int c;
  29313. int i;
  29314. int ntree = 256;
  29315. short h1, h2;
  29316.  
  29317. if (argc < 2)
  29318. { fprintf(stderr,"usage: %s infile \n",argv[0]);
  29319. exit(0);
  29320. }
  29321.  
  29322. if ((fin = fopen(argv[1],"rb")) == NULL)
  29323. { fprintf(stderr,"Unable to open %s for input\n",argv[1]);
  29324. exit(1);
  29325. }
  29326.  
  29327. if ((fout = fopen("htree.dat","wb")) == NULL)
  29328. { fprintf(stderr,"Unable to open htree.dat for output\n");
  29329. exit(1);
  29330. }
  29331.  
  29332. /* initialize character counts so all characters recognized
  29333. */
  29334. for (i=0; i<256; i++)
  29335. htcnt[i] = 1;
  29336.  
  29337. /* count character occurrence frequencies */
  29338. while ((c = fgetc(fin)) != EOF)
  29339. { htcnt[c]++;
  29340. }
  29341.  
  29342. /* build Huffman tree */
  29343. while(1)
  29344. { h1 = 0;
  29345. h2 = 0;
  29346. for (i=0; i<ntree; i++)
  29347. { if (i != h1)
  29348. { if (htcnt[i] > 0 && ht[i].parent == 0)
  29349. { if (h1 == 0 htcnt[i] < htcnt[h1])
  29350. { if (h2 == 0 htcnt[h1] < htcnt[h2])
  29351. h2 = h1;
  29352. h1 = i;
  29353. }
  29354.  
  29355. else if (h2 == 0 htcnt[i] < htcnt[h2])
  29356. h2 = i;
  29357. }
  29358. }
  29359. }
  29360. if (h2 == 0)
  29361. { root = h1;
  29362. break;
  29363. }
  29364. ht[h1].parent = ntree;
  29365. ht[h2].parent = ntree;
  29366. htcnt[ntree] = htcnt[h1] + htcnt[h2];
  29367. ht[ntree].right = h1;
  29368. ht[ntree].left = h2;
  29369. ntree++;
  29370. }
  29371.  
  29372. /* write out tree */
  29373. fwrite(&root, sizeof(root), 1, fout);
  29374. fwrite(ht, sizeof(ht), 1, fout);
  29375. fclose(fin);
  29376. fclose(fout);
  29377. }
  29378.  
  29379. /* End of File */
  29380.  
  29381.  
  29382. Listing 3 functions that compress a data record using Huffman encoding.
  29383. #include "hufftree.h"
  29384.  
  29385. /*
  29386. ------------------------ compress ----------------------------
  29387. /*
  29388.  
  29389. void compress(int inlen, char *bufin, int *outlen, char
  29390. *bufout)
  29391. {
  29392. int c, i;
  29393.  
  29394. bufout[(*outlen)++] = inlen;
  29395. for (i=0; i<inlen; i++)
  29396. { c = bufin[i];
  29397. encode((c & 255), 0, outlen, bufout);
  29398. }
  29399. emit(-1, outlen, bufout);
  29400.  
  29401. }
  29402.  
  29403.  
  29404. /*
  29405. ------------------------- encode -----------------------------
  29406. */
  29407.  
  29408. void encode(short h, short child, int *outlen, char *bufout)
  29409. {
  29410. if (ht[h].parent != 0)
  29411. encode(ht[h].parent, h, outlen, bufout);
  29412. if (child)
  29413. { if (child == ht[h].right)
  29414.  
  29415. emit(0, outlen, bufout);
  29416. else if (child == ht[h].left)
  29417. emit(1, outlen, bufout);
  29418. }
  29419. }
  29420.  
  29421.  
  29422. static char byt;
  29423. static int cnt;
  29424.  
  29425. /*
  29426. ------------------------- emit -------------------------------
  29427. */
  29428.  
  29429. void emit(int bit, int *outlen, char *bufout)
  29430. {
  29431. if (bit == -1)
  29432. { while (cnt != 8)
  29433. { byt = byt << 1;
  29434. cnt++;
  29435. }
  29436. bufout[(*outlen)++] = byt;
  29437. byt = 0;
  29438. cnt = 0;
  29439. return;
  29440. }
  29441. if (cnt == 8)
  29442. { bufout[(*outlen)++] = byt;
  29443. byt = 0;
  29444. cnt = 0;
  29445. }
  29446. byt = (byt << 1) bit;
  29447. cnt++;
  29448. }
  29449.  
  29450. /* End of File */
  29451.  
  29452.  
  29453. Listing 4 A function to decompress a data record compressed with Huffman
  29454. encoding.
  29455. #include <stdio.h>
  29456. #include "hufftree.h"
  29457.  
  29458. /*
  29459. -------------------------- decode -----------------------------
  29460. */
  29461.  
  29462. void decode(char *bufin, int *outlen, char *bufout)
  29463. { short h;
  29464. int obit;
  29465. int nin = 0, nout = 0;
  29466. int byt, cnt = 8;
  29467. unsigned char size;
  29468.  
  29469. size = (unsigned char)bufin[nin++];
  29470. while (nout< size)
  29471. { h = root;
  29472. while (ht[h].right != NULL)
  29473. { if (cnt == 8)
  29474. { byt = bufin[nin];
  29475.  
  29476. nin++;
  29477. cnt = 0;
  29478. }
  29479. obit = byt & 0x80;
  29480. byt <<= 1;
  29481. cnt++;
  29482. if (obit)
  29483. h = ht[h].left;
  29484. else
  29485. h = ht[h].right;
  29486. }
  29487. bufout[nout++] = h;
  29488. }
  29489. *outlen: nout;
  29490. }
  29491.  
  29492. /* End of File */
  29493.  
  29494.  
  29495. Listing 5 A program to illustrate the use of the data compression functions.
  29496. #include <stdio.h>
  29497. #include <string.h>
  29498. #define MAIN
  29499. #include "hufftree.h"
  29500.  
  29501. #define MAXBUF 1024
  29502.  
  29503. /*
  29504. ---------------------- filer - main -------------------------
  29505. */
  29506.  
  29507. main(int argc, char *argv[])
  29508. {
  29509. FILE *ftree, *indx, *in, *out;
  29510. long nrecs, end, zero = 0;
  29511. int inlen, outlen;
  29512. char bufin[MAXBUF];
  29513. char bufout[MAXBUF];
  29514. long ncharin = 0, ncharout = 0;
  29515.  
  29516. if (argc ! = 3)
  29517. { fprintf(stderr,"Usage: %s input output\n", argv[0]);
  29518. exit(0);
  29519. }
  29520.  
  29521. if ((in = fopen(argv[1], "rb")) == NULL)
  29522. { fprintf(stderr,"Unable to open input file %s\n", argv[1]);
  29523. exit(1);
  29524. }
  29525.  
  29526. if ((out = fopen(argv[2], "wb")) == NULL)
  29527. { fprintf(stderr,"Unable to open output file %s\n", argv[2]);
  29528. exit(1);
  29529. }
  29530.  
  29531. /* read in Huffman tree */
  29532. if ((ftree = fopen("htree.dat","rb")) == NULL)
  29533. { fprintf(stderr,"Unable to open htree.dat\n");
  29534. exit(1);
  29535.  
  29536. }
  29537. else
  29538. { fread(&root, sizeof(root), 1, ftree);
  29539. fread(ht, sizeof(ht), 1, ftree);
  29540. fclose(ftree);
  29541. }
  29542.  
  29543. /* create index file */
  29544. if ((indx = fopen("index", "wb")) == NULL)
  29545. { fprintf(stderr,"Unable to open index file index\n");
  29546. exit(1);
  29547. }
  29548. else
  29549. { fwrite(&zero, sizeof(zero), 1, indx);
  29550. fwrite(&zero, sizeof(zero), 1, indx);
  29551. nrecs = 0;
  29552. end = 0;
  29553. }
  29554.  
  29555. while (fgets(bufin, MAXBUF-1, in) != NULL)
  29556. { inlen = strlen(bufin);
  29557. ncharin += inlen;
  29558. outlen = 0;
  29559. compress(inlen, bufin, &outlen, (char *)bufout);
  29560. ncharout += outlen;
  29561. nrecs++;
  29562. end += outlen;
  29563. fwrite(&end, sizeof(end), 1, indx);
  29564. fwrite(bufout, sizeof(char), outlen, out);
  29565. }
  29566. rewind(indx);
  29567. fwrite(&nrecs, sizeof(nrecs), 1, indx);
  29568.  
  29569. printf("avg chars/record in: %.2f out:%.2f\n",
  29570. (float)ncharin/nrecs, (float)ncharout/nrecs);
  29571. printf("compression ratio: %.3f\n",
  29572. (float)ncharout/(float)ncharin);
  29573. }
  29574.  
  29575. /* End of File */
  29576.  
  29577.  
  29578. Listing 6 A program to illustrate the use of the data decompression function.
  29579. #include <stdio.h>
  29580. #define MAIN
  29581. #include "hufftree.h"
  29582.  
  29583. #define MAXBUF 1024
  29584.  
  29585. /*
  29586. ---------------------- unfiler - main -------------------------
  29587. */
  29588.  
  29589. main(int argc, char *argv[])
  29590. {
  29591. FILE *ftree, *indx, *in, *out;
  29592. char bufin[MAXBUF];
  29593. char bufout[MAXBUF];
  29594. long ofst, nrecs, start, end;
  29595.  
  29596. int i, len, outlen;
  29597.  
  29598. if (argc != 3)
  29599. { fprintf(stderr,"Usage: %s input \n", argv[0]);
  29600. exit(0);
  29601. }
  29602.  
  29603. if ((in = fopen(argv[1], "rb")) == NULL)
  29604. { fprintf(stderr,"Unable to open input file %s\n", argv[1]);
  29605. exit(l);
  29606. }
  29607.  
  29608. if ((out = fopen(argv[2], "wb")) == NULL)
  29609. { fprintf(stderr,"Unable to open output file %s\n", argv[2]);
  29610. exit(l);
  29611. }
  29612.  
  29613. if ((ftree = fopen("htree.dat","rb")) == NULL)
  29614. { fprintf(stderr,"Unable to open htree.dat\n");
  29615. exit(1);
  29616. }
  29617. else
  29618. { fread(&root, sizeof(root), 1, ftree);
  29619. fread(ht, sizeof(ht), 1, ftree);
  29620. fclose(ftree);
  29621. }
  29622.  
  29623. if ((indx = fopen("index", "rb")) == NULL)
  29624. { fprintf(stderr,"Unable to open index file\n");
  29625. exit(1);
  29626. }
  29627. else
  29628. { fread(&nrecs, sizeof(nrecs), 1, indx);
  29629. }
  29630.  
  29631. for (i=0; i<nrecs; i++)
  29632. {
  29633. ofst: sizeof(nrecs) + sizeof(start) * i;
  29634. fseek(indx, ofst, SEEK_SET);
  29635. fread(&start, sizeof(start), 1, indx);
  29636. fread(&end, sizeof(end), 1, indx);
  29637. fseek(in, sizeof(char) * (start), SEEK_SET)
  29638. len = end - start;
  29639. fread(bufin, sizeof(char), len, in);
  29640. decode(bufin, &outlen, bufout);
  29641. bufout[outlen] = '\O';
  29642. fprintf(out,"%s",bufout);
  29643. }
  29644.  
  29645. }
  29646.  
  29647. /* End of File */
  29648.  
  29649.  
  29650.  
  29651.  
  29652.  
  29653.  
  29654.  
  29655.  
  29656.  
  29657.  
  29658.  
  29659.  
  29660.  
  29661.  
  29662.  
  29663.  
  29664.  
  29665.  
  29666.  
  29667.  
  29668.  
  29669.  
  29670.  
  29671.  
  29672.  
  29673.  
  29674.  
  29675.  
  29676.  
  29677.  
  29678.  
  29679.  
  29680.  
  29681.  
  29682.  
  29683.  
  29684.  
  29685.  
  29686.  
  29687.  
  29688.  
  29689.  
  29690.  
  29691.  
  29692.  
  29693.  
  29694.  
  29695.  
  29696.  
  29697.  
  29698.  
  29699.  
  29700.  
  29701.  
  29702.  
  29703.  
  29704.  
  29705.  
  29706.  
  29707.  
  29708.  
  29709.  
  29710.  
  29711.  
  29712.  
  29713.  
  29714.  
  29715.  
  29716.  
  29717.  
  29718.  
  29719. Code Complete
  29720.  
  29721.  
  29722. Tommy Usher
  29723.  
  29724.  
  29725. Tommy Usher is president of RENT-A-HACK, a low-cost computer consulting firm.
  29726. He also does freelance programming. He can be reached at hacker3000@ao1.com.
  29727.  
  29728.  
  29729. Code Complete by Steve McConnell provides a good overview of a wide range of
  29730. programming topics. Though the book is described on the cover as "A Practical
  29731. Guide to Software Construction," in fact it ventures well beyond the practical
  29732. and into what McConnell might call the "philosophy" of good programming. Code
  29733. Complete doesn't provide all of the answers, but is a good place to start.
  29734.  
  29735.  
  29736. Audience
  29737.  
  29738.  
  29739. The author lists three groups who he feels should read this book: experienced
  29740. programmers, who want a comprehensive guide to software construction,
  29741. self-taught programmers, who are looking to learn more effective programming
  29742. practices, and students making the transition from an academic environment to
  29743. a professional one.
  29744.  
  29745.  
  29746. Content
  29747.  
  29748.  
  29749. The book is divided into eight sections:
  29750. 1. Laying the Foundation
  29751. 2. Design
  29752. 3. Data
  29753. 4. Control
  29754. 5. Constant Considerations
  29755. 6. Quality Improvement
  29756. 7. Final Steps
  29757. 8. Software Craftsmanship
  29758. Each of these sections contains several chapters on more specific topics.
  29759. McConnell uses special symbols throughout the book to alert the reader to key
  29760. points, hard data, suggestions for further reading, and what he calls coding
  29761. horrors. (This last item provides examples of how not to program.) Many of the
  29762. chapters include a check list designed to serve as a quick reference for the
  29763. chapter's contents.
  29764. The first section, "Laying the Foundation," deals with the concept of software
  29765. construction and provides a philosophical basis for the material which
  29766. follows. It begins with a definition of software construction, followed by a
  29767. discussion of the various metaphors that programmers use to understand
  29768. programming. (Does one "build" a program or "grow" it?) The author then
  29769. discusses the prerequisites to coding.
  29770. The next section deals with the design of software. Here McConnell introduces
  29771. concepts such as routines, modules, and Program Design Language (PDL). The
  29772. section includes a discussion of design methodologies, with special emphasis
  29773. on structured and object-oriented design. It concludes with a description of
  29774. "round-trip design," which seeks to combine the ideas of the other design
  29775. methodologies and apply the best-suited method to each problem.
  29776. The "Data" section puts the concept of data structures in practical terms. The
  29777. emphasis is not on the structures themselves but on how to use them
  29778. efficiently. Subjects covered include general issues, variable names, and data
  29779. types.
  29780. The "Control" section begins with straight-line code and moves through the
  29781. various types of control structures, including conditionals, loops, and
  29782. unusual control structures such as goto, return, and recursion.
  29783. The chapter on unusual control structures is one of the best in the book and
  29784. possibly one of the most controversial. The author provides one of the best
  29785. discussions of the goto issue I have read. He is balanced but cautious in his
  29786. treatment of goto. His conclusion is that in nine out of ten cases, gotos can
  29787. be easily eliminated. Of the remaining one in ten, nine out of those ten can
  29788. be eliminated with reasonable effort. That leaves one in 100 gotos that are
  29789. actually necessary. This may annoy those who feel gotos must never be used,
  29790. but he makes a reasoned and logical argument for his position. Further, he
  29791. provides reasonable guidelines for the proper use of gotos.
  29792. Another area where he provides useful information is when and how to use
  29793. recursion. An interesting section shows that the classic textbook examples
  29794. illustrating recursion -- calculating factorials and Fibonacci numbers -- are
  29795. both confusing and misleading. The section concludes with a discussion of
  29796. general control issues, and a closer look at structured programming.
  29797. The "Constant Considerations" section covers issues that programmers face
  29798. everyday, like layout, style, documentation, tools, and management. Here
  29799. again, McConnell tackles areas of controversy (that some programmers approach
  29800. with religious fervor) with balance and moderation. His ideas on program
  29801. layout and style will not please everyone -- for example, I prefer a different
  29802. style of indentation. Still, they deserve consideration and provide
  29803. interesting ideas. On the subject of comments, he tells an amusing story about
  29804. a group of philosophers debating their various philosophies of commenting.
  29805. Other chapters deal with programming tools, the relationship between size and
  29806. software construction, and management. Management is examined from both the
  29807. manager's and the programmer's points of view.
  29808. The next section is called "Quality Improvement." In this section the author
  29809. first gives an overview of software quality, then addresses some specific
  29810. subjects. Reviewing, unit testing, and debugging are examined in this section.
  29811. Each subject is treated in depth and McConnell suggests techniques and tools
  29812. that can help ensure the quality of a program.
  29813. Some additional topics related to quality improvement are discussed in the
  29814. next section, "Final Steps." Here the emphasis is on system integration, code
  29815. tuning, and software evolution. In a world of increasingly larger and larger
  29816. software packages, these are important chapters. One interesting and
  29817. potentially controversial concept introduced in the chapter on integration is
  29818. evolutionary delivery. This scheme calls for the developer to deliver the
  29819. software at successive levels of completion. The initial delivery is the core
  29820. of the ultimate product. Subsequent deliveries add capabilities, improve
  29821. interfaces and performance, and ultimately lead to a fully functional product.
  29822. These techniques seem to me more suited to development for in-house use but
  29823. the author is advocating them for commercial releases. His discussion of
  29824. evolutionary delivery almost seems a defense of releasing an unfinished
  29825. product with the intention of insuring future sales. (During the development
  29826. of a spreadsheet, for example, file operations are not added until the third
  29827. delivery.) This chapter seems to provide a solid basis for the truism that one
  29828. should never purchase any software with a version number ending in zero.
  29829. The last section of the book is on "Software Craftsmanship." The first chapter
  29830. in this section deals with what may seem a strange topic: personal character.
  29831. McConnell argues that character is a legitimate topic of discussion and
  29832. proceeds to describe the characteristics of good programmers. The next chapter
  29833. deals with several different themes related to software craftsmanship:
  29834. complexity, processes, iteration, and religion. By religion he of course
  29835. refers to the various issues, such as use of goto, layout style, and program
  29836. structure, that programmers become emotional about. The book ends with a real
  29837. gem of a chapter called "Where to Go for More Information." Here he gives
  29838. suggestions, both specific and general, for various books that should be in
  29839. the library of the software professional. He also suggests periodicals,
  29840. professional organizations, and sources for books on programming.
  29841.  
  29842.  
  29843. Commentary
  29844.  
  29845.  
  29846. Whatever your background, "Code Complete" has something to offer you. Even the
  29847. most experienced C programmer will find ideas for improvement. The author
  29848. writes in an entertaining style and makes good use of code segments to
  29849. illustrate his points.
  29850. Not everyone will be pleased with everything McConnell says. Since he deals
  29851. with subjects that many programmers are very emotional about, some readers
  29852. will no doubt object to his suggestions. Still, this book will benefit any
  29853. programmer who approaches it with an open mind. Even when I disagreed with the
  29854. author's approach, I could see his point. I was pleasantly surprised by his
  29855. willingness to challenge coventional wisdom.
  29856. The book is very well organized. Each chapter is cross-referenced to areas of
  29857. similar interest, and references to other works are in the margins rather than
  29858. at the end of the chapters. (It might be interesting to covert this book to an
  29859. online form. The extensive cross-references almost seem to have been created
  29860. with this in mind.)
  29861. Code Complete should be required reading for anyone who plans to begin or
  29862. continue a career in software development. It won't teach you how to be a C
  29863. programmer, but it could help make you a more productive one.
  29864. Title: Code Complete
  29865. Author: Steve McConnell
  29866. Publisher: Microsoft Press
  29867. Price: $35.00
  29868. ISBN: 1-55615-484-4
  29869. Pages: 880
  29870.  
  29871.  
  29872.  
  29873.  
  29874.  
  29875.  
  29876.  
  29877.  
  29878.  
  29879.  
  29880.  
  29881.  
  29882.  
  29883.  
  29884.  
  29885.  
  29886.  
  29887.  
  29888.  
  29889.  
  29890.  
  29891.  
  29892.  
  29893.  
  29894.  
  29895.  
  29896.  
  29897.  
  29898.  
  29899.  
  29900.  
  29901.  
  29902.  
  29903.  
  29904.  
  29905.  
  29906.  
  29907.  
  29908.  
  29909.  
  29910.  
  29911.  
  29912.  
  29913.  
  29914.  
  29915.  
  29916.  
  29917.  
  29918.  
  29919.  
  29920.  
  29921.  
  29922.  
  29923.  
  29924.  
  29925.  
  29926.  
  29927.  
  29928.  
  29929.  
  29930.  
  29931.  
  29932.  
  29933.  
  29934.  
  29935. Standard C
  29936.  
  29937.  
  29938. Introduction to lostreams
  29939.  
  29940.  
  29941.  
  29942.  
  29943. P.J. Plauger
  29944.  
  29945.  
  29946. P.J. Plauger is senior editor of The C Users Journal. He is convenor of the
  29947. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  29948. latest books are The Standard C Library, and Programming on Purpose (three
  29949. volumes), all published by Prentice-Hall. You can reach him at
  29950. pjp@plauger.com.
  29951.  
  29952.  
  29953. The largest single component of the Standard C++ library is the package called
  29954. "iostreams." It consists of a whole slew of classes that work together to make
  29955. input and output look simple. The commonest application is probably the
  29956. classic:
  29957. #include <iostream>
  29958. .....
  29959. cout << "Hello, world." << endl;
  29960. which writes the message Hello, world. followed by a newline character to the
  29961. standard output stream (same as stdout).
  29962. This simple expression is the tip of a rather large iceberg. Let's look a bit
  29963. below the surface.
  29964. As you might guess, the header <iostream> declares all the necessary classes.
  29965. (Existing implementations of C++ may append a .h, or a .hpp, to the header
  29966. name.) The class we care about for this example is ostream, each of whose
  29967. objects controls a stream into which you can insert characters. That stream
  29968. can be an output file, a text string that grows dynamically in memory, or lots
  29969. of other things.
  29970. In this particular case, cout is a static object of class ostream whose name
  29971. has global linkage. It conspires rather intimately with stdout, the familiar
  29972. Standard C library object of type pointer to FILE, to help you write formatted
  29973. text to the standard output stream. You can freely intermix expressions such
  29974. as the one above with more traditional calls such as to putchar or printf and
  29975. get the result you'd expect.
  29976. (Translation: you don't have to worry about two different buffering mechanisms
  29977. saving up characters and writing reordered blocks of characters to the
  29978. standard output stream. Again, existing implementations don't always make this
  29979. promise, at least not unless you perform some magic incantation at run time.
  29980. And then you can often expect a degradation of performance. The draft C++
  29981. Standard hopes to encourage better synchronization of C and C++ I/O, if only
  29982. to the standard streams.)
  29983. The header <iostream> declares two similar objects of class ostream:
  29984. cerr, which controls writes of unbuffered text to the standard error stream,
  29985. in cooperation with stderr, and
  29986. clog, which also cooperates with stderr but which won't necessarily force text
  29987. to be written out at the end of each insertion operation.
  29988.  
  29989.  
  29990. Inserters
  29991.  
  29992.  
  29993. Now let's take a closer look at the first part of the example expression:
  29994. cout << "Hello, world." .....
  29995. The class ostream overloads the left-shift operator repeatedly with member
  29996. functions to provide a rich set of inserters. These each encode a right-hand
  29997. operand by various rules (akin to the conversion specifications for printf),
  29998. then insert the encoded text in the stream controlled by the ostream object.
  29999. In this particular case, the inserter called is:
  30000. ostream& operator<<(const char* s);
  30001. This function knows to write the null-terminated string pointed to by s to the
  30002. output stream controlled by cout. Put another way, the function inserts each
  30003. of the characters from the string into the controlled stream up to but not
  30004. including the terminating null.
  30005. More complicated inserters turn binary integer and floating-point values into
  30006. sequences of characters that human beings can read. For example, the inserter:
  30007. ostream& operator<<(int n);
  30008. lets you write expressions like:
  30009. cout < i;
  30010. that inserts, say, the sequence -123 in the controlled stream to represent the
  30011. value --123 stored in the integer object i.
  30012. C programmers have been doing the same sort of thing for decades by writing:
  30013. printf("%s %d\n", s, i);
  30014. So what's the big deal? Well, the inserter approach offers a few advantages:
  30015. The translator picks the appropriate inserter to match the right hand operand
  30016. type. No need to make sure that conversion specifiers in a format string line
  30017. up with the proper arguments. There is that much less chance that the value
  30018. will be interpreted incorrectly.
  30019. The notation is often convenient. You can string inserters out left to right,
  30020. as in the original example above, to perform a series of insertions one after
  30021. the other.
  30022. The notation can be augmented in various clever ways, as with the end1 in the
  30023. original example.
  30024.  
  30025.  
  30026. Manipulators
  30027.  
  30028.  
  30029. I won't elaborate much on that last point in this column. All you need to know
  30030. for now is that inserting endl in an output stream has the effect of inserting
  30031. a newline character ('\n') then flushing output to the controlled stream. It
  30032. is but one of many interesting manipulators you can use with the inserter
  30033. notation.
  30034. Other manipulators make up for one of the shortcomings of the inserter
  30035. notation. Remember that with printf you can write some pretty fancy conversion
  30036. specifications, such as:
  30037. printf("%+10d\n", i );
  30038. to force a plus sign on positive output and pad the generated text to (at
  30039. least) ten characters. But operator<< takes only two operands. There is no
  30040. place to smuggle in that extra formatting information.
  30041. The solution is to squirrel away the extra information in the cout object
  30042. before performing the insertion. You can do this by calling member functions,
  30043. as in:
  30044.  
  30045. cout.setf(showpos), cout.width(10);
  30046. cout < i;
  30047. Or you can use still more magic manipulators to achieve the same effect, as
  30048. in:
  30049. cout << showpos << setw(10) << i;
  30050. As you can see, it's possible to have manipulators that take arguments. But
  30051. that involves even more chicanery -- a topic for much later in this series.
  30052. (In case you're wondering, the effect of showpos endures for subsequent
  30053. insertions, in either of the above forms. I won't show how to turn it off
  30054. here. But the field width evaporates after the first inserter that makes use
  30055. of it.) 
  30056.  
  30057.  
  30058. Extractors
  30059.  
  30060.  
  30061. You can probably guess what's coming next. The header <iostream> also supports
  30062. reading and decoding text from various streams, including input files. It
  30063. declares the object cin, which helps you extract characters from the standard
  30064. input stream, in cooperation with stdin. As you might further guess, this
  30065. object is of class istream, the obvious companion to ostream.
  30066. Thus, you can write code involving extractors, such as:
  30067. int n;
  30068. cin >> n;
  30069. to read a sequence of characters and decode them by the usual rules for
  30070. encoded integer input. The "usual" rules are much as for the function scanf:
  30071. Skip any leading whitespace.
  30072. Gobble one or more characters that look like a valid encoded integer and
  30073. convert them to int representation.
  30074. If no such characters are found, or if the result can't be properly
  30075. represented, report a failure.
  30076. One small difference exists between inserters and extractors. You can insert
  30077. the value of an expression into a stream. (This is usually called an "r-value"
  30078. by old-line C programmers.) But you extract from a stream a value into an
  30079. object. (Those same C programmers would call this an "l-value," but the times
  30080. and the terms they are a-changing.) A corresponding difference appears in C --
  30081. you call printf with arbitrary expressions for value arguments and you call
  30082. scanf with pointer arguments to designate the objects to store into.
  30083. In C++, you declare extractors with reference arguments, as in:
  30084. istream& operator>>(int& n);
  30085. That ampersand lets you write a bald n, but still ensures that a real live
  30086. 1-value gets bound to the corresponding parameter within the function. No
  30087. worry about null pointers or other pointer type mismatches.
  30088. You can also play tricks with extractors, by the way, much like that end1
  30089. shorthand I showed earlier. If all you want to do, for example, is consume any
  30090. pending white space from the standard input stream, you can write: 
  30091. cin >> ws;
  30092. and the job is done. Similarly, you can communicate various bits of formatting
  30093. information through other manipulators. much as with output streams. Once
  30094. again, I won't begin to explain the magic behind that bald ws manipulator.
  30095. Just note for now that such tricks are possible. 
  30096.  
  30097.  
  30098. A Little History
  30099.  
  30100.  
  30101. These iostreams have several clear advantages over the formatted I/O functions
  30102. of the Standard C library. Little wonder that every implementation of C++ has
  30103. for years offered some version of iostreams, however much the implementation
  30104. may vary in its support for other common library classes. Jerry Schwarz, now
  30105. at Lucid Technology, gets credit for developing the earliest version of
  30106. iostreams, for helping it become so widespread, and for seeing the package
  30107. through several major revisions. He is also responsible for drafting the
  30108. specification of iostreams in the draft standard C++ library.
  30109. Unfortunately, little has been written on the detailed architecture of
  30110. iostreams. About the only commercially available guide is a book by Steve
  30111. Teale [1], which deals with a slightly dated version of the package. Since the
  30112. draft C++ standard progresses even beyond the current field version, Teale's
  30113. book offers only limited guidance. Still, it's better than what you typically
  30114. get from the vendor of a C++ translator.
  30115. For many class libraries this lack of information would not be a problem. But
  30116. Schwarz designed iostreams to be extensible in several important ways: 
  30117. You can overload operator<< to define additional inserters, or operator>> to
  30118. define additional extractors for classes you define.
  30119. You can define a host of manipulators that work with objects of class ostream,
  30120. class istream, or both.
  30121. You can derive new classes from class streambuf, then over-ride several of its
  30122. virtual member functions, to control sources and sinks of characters of your
  30123. own devising.
  30124. Such power is not without its complexity. And complexity can be mastered
  30125. safely only with careful guidance. To date, programmers have relied on access
  30126. to bits and pieces of library source code to get that guidance. Where such
  30127. code is not available, or where it varies among implementations, adequate
  30128. guidance has been lacking. Standardizing iostreams is thus a major step toward
  30129. helping the package realize its full potential.
  30130.  
  30131.  
  30132. Base Class ios
  30133.  
  30134.  
  30135. Let's begin with some basic architecture. Classes ostream and istream have
  30136. several requirements in common:
  30137. Both must control a stream through the agency of some object of class
  30138. streambuf.
  30139. Both must maintain some notion of the state of the controlled stream,
  30140. including a history of any errors that have occurred and how to report future
  30141. errors.
  30142. Both must memorize a host of formatting options, as I described earlier.
  30143. Both must define a number of common nested types for describing the member
  30144. functions and objects needed to effectuate the above requirements.
  30145. To provide all these services, both classes derive from the virtual public
  30146. base class ios. Listing 1 shows how class ios is declared in the draft C++
  30147. Standard. Two kinds of members are commented out:
  30148. Those labeled exposition only are merely indicative of what kind of
  30149. information the class must maintain. Don't expect an actual implementation to
  30150. have exactly such a member with exactly such a name. (The italicized names
  30151. aren't even reserved.)
  30152. Those labeled optional can be present in an implementation for backward
  30153. compatibility with some widespread existing practice. A conforming
  30154. implementation may, however, choose to omit such members.
  30155. Note that class ios is a virtual base for both ostream and istream. That is
  30156. more a matter of compatibility with past practice than of necessity. Some
  30157. implementations control a stream that can be both read and written by
  30158. declaring an object of class iostream, defined something like:
  30159. class iostream
  30160. : public istream, ostream {
  30161. ..... };
  30162. Were the base ios not virtual, this class would end up with two such
  30163. subobjects, not just one. That would lead to all sorts of confusion in trying
  30164. to control the bidirectional stream. Making the base virtual does add a bit of
  30165. complexity here and there, particularly with initialization, but it permits
  30166. the traditional definition of class iostream for those who want to keep using
  30167. it.
  30168. This class is not a part of the draft C++ standard, however, because it is no
  30169. longer necessary. The preferred way to control a bidirectional stream is with
  30170. two separate objects, one of class ostream and one of class istream. Both
  30171. point to the same streambuf object, which is the only agent who really has to
  30172. know that the stream can be both read and written. (That is part of the reason
  30173. why an object of class ios contains a pointer to a separate streambuf object,
  30174. instead of the object itself.)
  30175.  
  30176.  
  30177.  
  30178. In Times to Come
  30179.  
  30180.  
  30181. Class ios takes a lot of declaring, as you can see from Listing 1. For all
  30182. that, it is neither a big nor a very complex class. Still, all that declaring
  30183. demands a comparable amount of explaining. And class ios does lie at the heart
  30184. of the entire iostreams class hierarchy. I plan to devote the next installment
  30185. of this column to a careful study of this class, and the subtler implications
  30186. of its semantics. Only after such a detailed introduction do classes ostream
  30187. and istream begin to make sense.
  30188. Class streambuf is equally fundamental to iostreams. You can get quite a lot
  30189. of use out of iostreams without ever declaring a streambuf object in anger.
  30190. But if you want to know how the whole works hangs together, or if you want to
  30191. extend iostreams in nontrivial ways, you must know how this class behaves in
  30192. detail.
  30193. Manipulators are yet another topic. Many are simple and easy to explain once
  30194. you know the basics of the classes ios, ostream, and istream. But all those
  30195. manipulators with arguments derive from one of several "interesting" template
  30196. classes. (In many existing implementations, they are built atop even more
  30197. interesting macros.)
  30198. Two library classes show some of the power of class streambuf. Class
  30199. strstreambuf provides capabilities very akin to the Standard C library
  30200. function sprintf. You can use inserters and extractors to manipulate in-memory
  30201. text strings. Class stringstreambuf is similar, except that it eases
  30202. conversion between such in-memory strings and objects of the standard library
  30203. class string. (I'll discuss class string in detail much later.)
  30204. Finally, we return to the objects that manipulate external files. Class
  30205. filebuf lets you open files by name, much as with fopen, then manipulate them
  30206. as iostreams. And the objects cin, cout, cerr, and clog have their own tale as
  30207. well. It turns out that initializing these creatures, as required by the draft
  30208. C++ Standard, is no mean feat.
  30209. The description of the iostreams facilities occupies about half the library
  30210. portion of the draft C++ Standard. It's going to take quite some time to
  30211. review it in adequate detail. Bear with me.
  30212. References
  30213. [1] Steve Teale, C++ IOStreams Handbook, Addison-Wesley, 1993.
  30214.  
  30215. Listing 1 Class ios
  30216. class ios {
  30217. public:
  30218. class failure public: xmsg {
  30219. public:
  30220. failure(const char* where_val = 0, const char*
  30221. why_val = 0);
  30222. virtual ~failure();
  30223. protected:
  30224. // virtual void do_raise(); inherited
  30225. };
  30226. typedef T1 fmtflags;
  30227. static const fmtflags dec;
  30228. static const fmtflags fixed;
  30229. static const fmtflags hex;
  30230. static const fmtflags internal;
  30231. static const fmtflags left;
  30232. static const fmtflags oct;
  30233. static const fmtflags right;
  30234. static const fmtflags scientific;
  30235. static const fmtflags showbase;
  30236. static const fmtflags showpoint;
  30237. static const fmtflags showpos;
  30238. static const fmtflags skipws;
  30239. static const fmtflags unitbuf;
  30240. static const fmtflags uppercase;
  30241. static const fmtflags adjustfield;
  30242. static const fmtflags basefield;
  30243. static const fmtflags floatfield;
  30244. typedef T2 iostate;
  30245. static const iostate badbit;
  30246. static const iostate eofbit;
  30247. static const iostate failbit;
  30248. static const iostate goodbit;
  30249. typedef T3 openmode;
  30250. static const openmode app;
  30251. static const openmode ate;
  30252. static const openmode binary;
  30253. static const openmode in;
  30254. static const openmode out;
  30255. static const openmode trunc;
  30256. typedef T4 seekdir;
  30257. static const seekdir beg;
  30258. static const seekdir cur;
  30259. static const seekdir end;
  30260. // typedef T5 io_state; optional
  30261. // typedef T6 open_mode; optional
  30262.  
  30263. // typeder T7 seek_dir; optional
  30264. class Init {
  30265. public:
  30266. Init();
  30267. ~Init();
  30268. private:
  30269. // static int init_cnt; exposition only
  30270. };
  30271. ios(streambuf* sb_arg);
  30272. virtual ~ios();
  30273. operator void*() const
  30274. int operator!() const
  30275. ios& copyfmt(const ios& rhs);
  30276. ostream* tie() const;
  30277. ostream* tie(ostream* tiestr_arg);
  30278. streambuf* rdbuf() const;
  30279. streambuf* rdbuf(streambuf* sb_arg);
  30280. iostate rdstate() const;
  30281. void clear(iostate state_arg = 0);
  30282. // void clear(io_state state_arg = 0); optional
  30283. void setstate(iostate state_arg);
  30284. // void setstate(io_state state_arg); optional
  30285. int good() const;
  30286. int eof() const;
  30287. int fail() const;
  30288. int bad() const;
  30289. iostate exceptions() const;
  30290. void exceptions(iostate except_arg);
  30291. // void exceptions(io_state except_arg); optional
  30292. fmtflags flags() const;
  30293. fmtflags flags(fmtflags fmtfl_arg);
  30294. fmtflags setf(fmtflags fmtfl_arg);
  30295. fmtflags setf(fmtflags fmtfl_arg, fmtflags mask);
  30296. void unsetf(fmtflags mask);
  30297. int fill() const;
  30298. int fill(int ch);
  30299. int precision() const;
  30300. int precision(int prec_arg);
  30301. int width() const;
  30302. int width(int wide_arg);
  30303. static int xalloc();
  30304. long& iword(int index_arg);
  30305. void*& pword(int index_arg);
  30306. protected:
  30307. ios();
  30308. init(streambuf* sb_arg);
  30309. private:
  30310. // streambuf* sb; exposition only
  30311. // ostream* tiestr; exposition only
  30312. // iostate state; exposition only
  30313. // iostate except; exposition only
  30314. // fmtflags fmtfl; exposition only
  30315. // int prec; exposition only
  30316. // int wide; exposition only
  30317. // char fillch; exposition only
  30318. // static int index; exposition only
  30319. // int* iarray; exposition only
  30320. // void** parray; exposition only
  30321. };
  30322.  
  30323.  
  30324. // End of File
  30325.  
  30326.  
  30327.  
  30328.  
  30329.  
  30330.  
  30331.  
  30332.  
  30333.  
  30334.  
  30335.  
  30336.  
  30337.  
  30338.  
  30339.  
  30340.  
  30341.  
  30342.  
  30343.  
  30344.  
  30345.  
  30346.  
  30347.  
  30348.  
  30349.  
  30350.  
  30351.  
  30352.  
  30353.  
  30354.  
  30355.  
  30356.  
  30357.  
  30358.  
  30359.  
  30360.  
  30361.  
  30362.  
  30363.  
  30364.  
  30365.  
  30366.  
  30367.  
  30368.  
  30369.  
  30370.  
  30371.  
  30372.  
  30373.  
  30374.  
  30375.  
  30376.  
  30377.  
  30378.  
  30379.  
  30380.  
  30381.  
  30382.  
  30383.  
  30384.  
  30385.  
  30386. Code Capsules
  30387.  
  30388.  
  30389. Visibility in C
  30390.  
  30391.  
  30392.  
  30393.  
  30394. Chuck Allison
  30395.  
  30396.  
  30397. Chuck Allison is a regular columnist with CUJ and a software architect for the
  30398. Family History Department of the Church of Jesus Christ of Latter Day Saints
  30399. Church Headquarters in Salt Lake City. He has a B.S. and M.S. in mathematics,
  30400. has been programming since 1975, and has been teaching and developing in C
  30401. since 1984. His current interest is object-oriented technology and education.
  30402. He is a member of X3Jl6, the ANSI C++ Standards Committee. Chuck can be
  30403. reached on the Internet at allison@decus.org, or at (801)240-4510.
  30404.  
  30405.  
  30406.  
  30407.  
  30408. What's in a Name?
  30409.  
  30410.  
  30411. Every token in a C program that begins with a letter or an underscore, other
  30412. than a keyword or macro, names one of the following entities:
  30413. data object
  30414. function
  30415. type definition (typedef)
  30416. tag for a structure, union, or enumeration
  30417. member of a structure, union
  30418. enumeration constant
  30419. label
  30420. These entities are active at certain times and places in your program,
  30421. depending on how and where their declarations appear. Whether or not you are
  30422. aware of it, when you declare an identifier you determine its scope, lifetime,
  30423. linkage, and namespace. In this article I will illustrate these inter-related
  30424. concepts as Standard C defines them.
  30425.  
  30426.  
  30427. Scope
  30428.  
  30429.  
  30430. The scope of an identifier is the region of your program's text where you can
  30431. use that identifier (in other words, where it is visible). There are four
  30432. types of scope in Standard C:
  30433. 1) Block -- a region within a pair of matching {}-braces that begins where the
  30434. declarator first appears and ends with the first subsequent closing brace
  30435. 2) Function Prototype -- the region in a function prototype from where the
  30436. identifier occurs to where the last closing parenthesis appears
  30437. 3) Function -- the entire body of a function
  30438. 4) File -- the region in a source file from where an identifier first appears
  30439. outside of any block to the end of the source file
  30440. Formal parameters have the same scope as if they were declared in the
  30441. outermost block of their function. Identifier names are optional in function
  30442. prototypes, and serve only for documentation if they appear. Block scope and
  30443. function prototype scope together are sometimes referred to as local scope.
  30444. In Listing 1, the optional identifier val serves as documentation only, and is
  30445. not visible outside the prototype for the function f. f's formal parameter i
  30446. is visible immediately after the opening brace that defines f. Since each
  30447. block introduces a new scope, the i initialized by j in the innermost block
  30448. temporarily hides the i in the outer block. The value of j is available to
  30449. initialize i in the inner block because j was declared first. If j had been
  30450. declared like this:
  30451. {
  30452. int j = i; /* outer i */
  30453. then j would have received the value 1. There is no conflict because the inner
  30454. i isn't visible until the next statement.
  30455. An identifier is visible as soon as its declarator is complete. The following
  30456. declaration is ill-formed:
  30457. int i = i;
  30458. Since int i is sufficient to declare an integer named i, the i on the right is
  30459. the same as on the left, and i is left uninitialized.
  30460. Only labels have function scope. There's a difference between function scope
  30461. and the scope of a function's outermost block. Labels which have function
  30462. scope are visible throughout the function, even before they are "declared,"
  30463. but identifiers with block scope are visible only after their point of
  30464. declaration and can be hidden by other declarations in nested blocks.
  30465. An identifier declared outside of any block or function parameter list has
  30466. file scope. Such identifiers are sometimes referred to as global, and are
  30467. visible from their point of declaration until the end of the translation unit,
  30468. unless they are hidden by an identifier with the same name having block scope.
  30469. The program in Listing 2 illustrates function and file scope. Since the
  30470. identifier i with file scope (the one initialized to 13) is visible only after
  30471. its declaration, it would be an error to try to use it in the main program.
  30472. The global i is not available anywhere in f1 since f1 has a parameter named i.
  30473. The innermost block of f1 in turn hides that parameter with its own i (the
  30474. types don't have to be the same). Since f2 declares no identifiers named i, it
  30475. has access to the global i. The declarations of fl and f2 in main inject those
  30476. names into the body of main only. It would be an error, for example, to call
  30477. f2 from f1.
  30478.  
  30479.  
  30480. Minimal Scoping
  30481.  
  30482.  
  30483. Thoughtful placement of declarations can greatly enhance the readability of a
  30484. program. Most programmers still seem to follow the convention, required by
  30485. languages such as FORTRAN and COBOL, of placing all declarations together in a
  30486. single section of the source code. This gives you the advantage of always
  30487. knowing where to look for variable definitions. But when a program gets large,
  30488. you spend a good deal of time flipping back and forth between the declaration
  30489. of an identifier and its point of use. Those of you who program in Microsoft
  30490. Windows know that Hungarian notation, a convention of encoding the type of an
  30491. identifier into its name, evolved as a means of compensating for the distance
  30492. between a name's declaration and its use. At the risk of inciting a flurry of
  30493. letters to the editor, I would like to suggest instead a simple technique
  30494. known to C++ programmers which, when coupled with modular design, renders
  30495. strange-looking Hungarian names unnecessary in most cases. I call it minimal
  30496. scoping. Simply put, it means to declare an identifier as close as possible to
  30497. its first use.
  30498. For example, what do you infer from the following program segment?
  30499. void f(void)
  30500.  
  30501. {
  30502. int i, j;
  30503. ...
  30504. }
  30505. Even though you may only use i and j in a small portion of f, the declaration
  30506. says that they are available everywhere within the function. Therefore, i and
  30507. j have more scope than they deserve. If, for example, i and j only apply under
  30508. certain conditions, you can easily limit their scope by declaring them within
  30509. an appropriate inner block:
  30510. void f(void)
  30511. {
  30512. ...
  30513. if (<some condition>)
  30514. {
  30515. int i, j;
  30516. /* only use i and j here */
  30517. }
  30518. ...
  30519. }
  30520. Another advantage to this practice is that i and j aren't even allocated if
  30521. the condition is false -- they don't exist outside their block. C++ encourages
  30522. minimal scoping by allowing declarations to appear anywhere a statement can,
  30523. as in:
  30524. for (int i = 0; i < n; ++i)
  30525. ...
  30526. The index i is visible from its point of declaration to the end of its block.
  30527. Minimal scoping aids readability because you don't even see identifiers until
  30528. you need to -- when they add meaning to the program.
  30529.  
  30530.  
  30531. Lifetime
  30532.  
  30533.  
  30534. The lifetime, or storage durations of an object is the period from the time it
  30535. is created to the time it is destroyed. Objects that have static duration are
  30536. created and initialized once, prior to program startup, and are destroyed when
  30537. the program terminates normally. Objects with file scope, as well as objects
  30538. declared with the static specifier at block scope, have static duration.
  30539. Listing 3 shows an example of the latter. The variable n is initialized once
  30540. at program startup and retains its last-assigned value throughout the program.
  30541. (Its scope, however, is just the body of the function count.)
  30542. Function parameters and objects declared within a block without the extern or
  30543. static specifier have automatic duration. Such objects are created anew every
  30544. time execution enters their block. Every time execution enters a block
  30545. normally, that is, not as the result of a goto, then any initialization you
  30546. may have specified is also performed. When execution falls through or jumps
  30547. past the end of a block, or returns from a function, all automatic variables
  30548. in that scope are destroyed.
  30549. The program in Listing 4 illustrates both static and automatic duration with
  30550. the familiar factorial function. The token n!, pronounced "n-factorial"
  30551. (without yelling), denotes the product of all positive integers up to and
  30552. including n. For example,
  30553. 3! = 3 x 2 x 1
  30554. 4! = 4 x 3 x 2 x 1
  30555. etc.
  30556. Most math textbooks give the following equivalent recursive definition
  30557. instead:
  30558. Click Here for Equation
  30559. You can render this definition concisely in C with the following recursive
  30560. function:
  30561. long fac(long n)
  30562. {
  30563. return (n <= 1) ? 1 : n * fac(n-1);
  30564. }
  30565. When n is greater than 1, fac calls itself recursively, with an argument equal
  30566. to one less than it started with. This action temporarily suspends the current
  30567. scope and creates a new one, with its own copy of n. This process continues
  30568. until the most deeply nested copy of n is equal to 1. This scope terminates
  30569. and returns 1 to the scope that called it, and so on up to the original
  30570. invocation. For example, consider the execution of the expression fac(3):
  30571. fac(3): return (3 <= 1) ? 1 : 3 * fac(2);
  30572. This calls fac(2):
  30573. fac(2): return (2 <= 1) ? 1 : 2 * fac(1);
  30574. which in turn calls fac(1):
  30575. fac(1): return (1 <= 1) ? 1 : fac(0);
  30576. which returns 1:
  30577. fac(1): return 1;
  30578. fac(2) now resumes and returns the following to fac(3):
  30579. fac(2): return 2 * 1;
  30580. which returns the value 6 to the original caller.
  30581. The program in listing 4 traces this recursive computation by wrapping the
  30582. factorial formula with statements to print the value coming into the function
  30583. and the computed value going out. This program keeps track of how deep the
  30584. recursion has nested with the static variable depth. Since depth has static
  30585. duration, it is allocated and initialized once prior to program startup and
  30586. retains its value across function calls (including recursive ones). Only
  30587. automatic variables, like n, are replicated with each recursive call. The auto
  30588. keyword is purely documentary, since all variables with block scope are
  30589. automatic by default.
  30590.  
  30591.  
  30592. Linkage
  30593.  
  30594.  
  30595. According to the rules of linkage, two same-named identifiers can refer to the
  30596. same object, even if they occupy different translation units. There are three
  30597. types of linkage in C:
  30598. 1) External linkage -- names across translation units in a program
  30599. 2) Internal linkage -- names throughout a single translation unit
  30600. 3) No linkage -- certain objects are unique, hence, they have no linkage
  30601. Both functions and global objects declared wihout the static keyword have
  30602. external linkage. There must be only one definition of each such object, but
  30603. there may be many declarations that refer to that definition. For example, if
  30604. the following declaration occurs at file scope in a file:
  30605.  
  30606. /* file1.c */
  30607. int x;
  30608. then it can used in another file at any scope where the following occurs:
  30609. /* file2.c */
  30610. extern int x;
  30611. The extern specifier in essence says, "find an object named x defined at file
  30612. scope." The extern specifier is not required to link to a function with
  30613. external linkage:
  30614. /* file1.c */
  30615. int f(void)
  30616. {
  30617. return 1;
  30618. }
  30619.  
  30620. /* file2.c*/
  30621. int f(void); /* extern specifier assumed for functions
  30622. - links to f in file1.c */
  30623. Although the C standard doesn't explicitly define it, you can think of objects
  30624. with external linkage as constituting a new scope: that of objects visible
  30625. across translation units. On the street this is known as program scope.
  30626. Functions and global objects declared with the static specifier have internal
  30627. linkage. Identifiers with internal linkage are visible only within their
  30628. translation unit. This use of the keyword static has little to do with the
  30629. static storage duration specifier discussed previously. It's a good idea to
  30630. commit the following pseudo-formulas to memory:
  30631. static + block scope == static storage duration
  30632. static + file scope == internal linkage
  30633. The first use of static alters lifetime, the second linkage. If you think this
  30634. is confusing, C++ muddies the waters further by introducing a third use of the
  30635. term (static class members). I'll spare you that one until next month.
  30636. Certain program entities, which are always unique, are said to have no
  30637. linkage. These entities include objects having block scope but no extern
  30638. specifier, function parameters, and anything other than function or object,
  30639. such as a label, tag name, member name, typedef name, or enumeration constant.
  30640. The source files in Listing 5 and Listing 6 comprise a single executable
  30641. program that illustrates the different types of linkage. Listing 5 is similar
  30642. to Listing 2 except that functions f1 and f2 are private to the source file,
  30643. and a function from the source file in Listing 6 is added to the executable
  30644. program. The integer i at file scope in Listing 5 has external linkage because
  30645. it does not carry the static specifier. A variable of the same name in another
  30646. file can refer to it if declared with the extern specifier, as Listing 6 does.
  30647. (It is an error to have two definitions of the same object with external
  30648. linkage, e.g., two i's modified by neither static nor extern.)
  30649. The functions f1 and f2 have internal linkage because they use the static
  30650. specifier. The float object named i in f1 has no linkage because it is
  30651. declared at block scope without the extern specifier. The integer j in Listing
  30652. 6 has internal linkage because of the static specifier, and the function f3
  30653. has external linkage because of the absence of the static specifier.
  30654. The following three lines from Listing 5 require particular explanation:
  30655. extern void f1(int); /* Internal Linkage */
  30656. extern void f2(void); /* Internal Linkage */
  30657. extern void f3(void); /* External Linkage */
  30658. Since the extern specifier means "link with something at file scope," the
  30659. declarations f1 and f2 in main link respectively with the functions of the
  30660. same name in the same file, which happen to have internal linkage. It is
  30661. important that you declare f1 and f2 static at file scope before the extern
  30662. references in main, or else the compiler will assume that they have external
  30663. linkage (like it does for f3), which conflicts with the actual function
  30664. definitions later in the file.
  30665.  
  30666.  
  30667. Namespaces
  30668.  
  30669.  
  30670. An identifier can play various roles in a C program. For example, in the
  30671. following excerpt, pair is both a function name and a structure tag:
  30672. struct pair {int x; int y;};
  30673.  
  30674. void pair(struct pair p)
  30675. {
  30676. printf("(%d,%d)\n",p.x,p.y);
  30677. }
  30678. The compiler keeps separate lists of identifiers used in different roles, so
  30679. there is no danger of ambiguity. These lists are called namespaces. There are
  30680. four different types of namespaces in standard C:
  30681. 1) labels
  30682. 2) tags for structures, unions, and enumerations
  30683. 3) members of structures and unions
  30684. 4) ordinary identifiers (i.e., all others: data objects, functions, types, and
  30685. enumeration constants).
  30686. Each function keeps its own list of labels. Each translation unit and each
  30687. block keeps its own set of namespaces for tags and for ordinary identifiers,
  30688. which is what allows an inner scope to hide like- named entities at outer
  30689. scopes. Each structure or union type keeps its own list of identifiers.
  30690. Enumeration constants belong to the space of ordinary identifiers, since you
  30691. use them like objects in a program.
  30692. The program in Listing 7 uses the following namespaces (arbitrary names are
  30693. mine):
  30694. tag-global -- struct-union-enum tags defined at file scope. A redefinition of
  30695. struct foo at block scope would hide the one from an enclosing scope.
  30696. global-foo-member -- the member names of the struct foo defined at file scope.
  30697. Any redefinitions of struct foo at block scope would essentially be new types,
  30698. and would keep their own list of member names.
  30699. ordinary-global -- any data objects, functions, typedefs or enumeration
  30700. constants defined at file scope (in this case, a struct foo object, which is
  30701. hidden by a like-named object in main).
  30702. ordinary-main-1 -- ordinary identifiers defined at the outer scope main (a
  30703. struct foo object which hides the global one).
  30704. ordinary-main-2 -- ordinary identifiers defined in the inner block of main
  30705. (struct foo x, and the integer foo, which hides the struct foo of the same
  30706. name in the enclosing scope).
  30707. label-main -- labels defined in main (foo:)
  30708. The comments in the source file indicate the namespace that claims each use of
  30709. the identifier foo. I hope you find this program a little (nay, a lot)
  30710. confusing. My monotonous overuse of a single identifier was for illustrative
  30711. purposes only. You should only reuse names for good reason -- and I can't
  30712. think of any at the moment, without discussing C++ overloading. If you see
  30713. such code in "real life," treat it like a sensitive government document:
  30714. DESTROY BEFORE READING!
  30715.  
  30716.  
  30717. Summary
  30718.  
  30719.  
  30720.  
  30721. There's a lot to a name in a C program. Each identifier has a scope (where it
  30722. is visible), a lifetime (when it is active), a linkage (whether remote uses of
  30723. the same name refer to the same entity), and a namespace (its role among
  30724. identifiers). If this article hasn't made sense to you (but I hope it has),
  30725. guess what: it gets worse! I can't think of a single concept I've discussed
  30726. here that C++ doesn't affect. Now you know the subject of next month's
  30727. article.
  30728.  
  30729. Listing 1 Illustrates local scope
  30730. /* scopel.c */
  30731.  
  30732. #include <stdio.h>
  30733.  
  30734. void f(int val);
  30735.  
  30736. main()
  30737. {
  30738. f(1);
  30739. return 0;
  30740. }
  30741.  
  30742. void f(int i)
  30743. {
  30744. printf("i == %d\n",i);
  30745. {
  30746. int j = 10;
  30747. int i = j;
  30748. printf("i == %d\n",i);
  30749. }
  30750. }
  30751.  
  30752. /* Output:
  30753. i == 1
  30754. i == 10
  30755. */
  30756.  
  30757. /* End of File */
  30758.  
  30759.  
  30760. Listing 2 Illustrates function and file scope
  30761. /* scope2.c */
  30762.  
  30763. #include <stdio.h>
  30764.  
  30765. main()
  30766. {
  30767. void fl(int i);
  30768. void f2(void);
  30769.  
  30770. f1(23);
  30771. f2();
  30772. return 0;
  30773. }
  30774.  
  30775. int i = 13;
  30776.  
  30777. void fl(int i)
  30778. {
  30779. for (;;)
  30780. {
  30781. float i = 33.0;
  30782.  
  30783. printf("%f\n",i);
  30784. goto exit;
  30785. }
  30786.  
  30787.  
  30788. exit:
  30789. printf("%d\n",i);
  30790. }
  30791.  
  30792. void f2(void)
  30793. {
  30794. printf("%d\n",i);
  30795. }
  30796.  
  30797. /* Output:
  30798. 33.000000
  30799. 23
  30800. 13
  30801. */
  30802.  
  30803. /* End of File */
  30804.  
  30805.  
  30806. Listing 3 Illustrates static storage duration
  30807. /* lifetime.c */
  30808.  
  30809. #include <stdio.h>
  30810.  
  30811. main()
  30812. {
  30813. int count(void);
  30814. int i;
  30815.  
  30816. for (i = 0; i < 5; ++i)
  30817. printf("%d\n",count( ));
  30818. return 0;
  30819. }
  30820.  
  30821. int count(void)
  30822. {
  30823. static int n = 0;
  30824.  
  30825. return ++n;
  30826. }
  30827.  
  30828. /* Output:
  30829. 1
  30830. 2
  30831. 3
  30832. 4
  30833. 5
  30834. */
  30835.  
  30836. /* End of File */
  30837.  
  30838.  
  30839. Listing 4 Illustrates recursion and storage duration
  30840. /* recurse.c */
  30841.  
  30842. #include <stdio.h>
  30843.  
  30844. main()
  30845. {
  30846.  
  30847. long n;
  30848. long fac(long);
  30849.  
  30850. fputs("Enter a small integer: ",stderr);
  30851. scanf("%ld%*c",&n);
  30852. printf("\n%ld! = %ld\n",n,fac(n));
  30853. return 0;
  30854. }
  30855.  
  30856. long fac(long n)
  30857. {
  30858. static int depth = 0;
  30859. auto long result;
  30860. void print_current(int,long);
  30861.  
  30862. print_current(++depth,n);
  30863. result = (n <= 1) ? i : n * fac(n-1);
  30864. print_current(depth--,result);
  30865.  
  30866. return result;
  30867. }
  30868.  
  30869. void print_current(int depth, long n)
  30870. {
  30871. int i;
  30872.  
  30873. /* Indent to show depth */
  30874. for (i = 0; i < depth; ++i)
  30875. fputs(" ",stdout);
  30876.  
  30877. printf("%ld\n",n);
  30878. }
  30879.  
  30880. /* Output:
  30881. Enter a small integer: 3
  30882. 3
  30883. 2
  30884. 1
  30885. 1
  30886. 2
  30887. 6
  30888.  
  30889. 3! = 6
  30890. */
  30891.  
  30892. /* End of File */
  30893.  
  30894.  
  30895. Listing 5 Links with linkage2.c
  30896. /* linkage1.c */
  30897.  
  30898. #include <stdio.h>
  30899.  
  30900. static void fl(int);
  30901. static void f2(void);
  30902.  
  30903. main()
  30904. {
  30905. extern void fl(int); /* Internal Linkage */
  30906.  
  30907. extern void f2(void); /* Internal Linkage */
  30908. extern void f3(void); /* External Linkage */
  30909.  
  30910. f1(23);
  30911. f2();
  30912. f3();
  30913. return 0;
  30914. }
  30915.  
  30916. int i = 13; /* External Linkage */
  30917.  
  30918. static void fl(int i) /* Internal Linkage */
  30919. {
  30920. for (;;)
  30921. {
  30922. float i = 33.0; /* No Linkage */
  30923.  
  30924. printf("%f\n",i );
  30925. goto exit;
  30926. }
  30927.  
  30928. exit: /* No linkage */
  30929. printf("%d\n", i );
  30930. }
  30931.  
  30932. static void f2(void) /* Internal Linkage */
  30933. {
  30934. printf("%d\n",i );
  30935. }
  30936.  
  30937. /* Output:
  30938. 33.000000
  30939. 23
  30940. 13
  30941. 16
  30942. */
  30943.  
  30944. /* End of File */
  30945.  
  30946.  
  30947. Listing 6 Links with linkage1.c
  30948. /* linkage2.c */
  30949.  
  30950. #include <stdio.h>
  30951.  
  30952. extern int i; /* External Linkage */
  30953.  
  30954. static int j = 3; /* Internal Linkage */
  30955.  
  30956. void f3(void) /* External Linkage */
  30957. {
  30958. printf("%d\n",i+j);
  30959. }
  30960.  
  30961. /* End of File */
  30962.  
  30963.  
  30964. Listing 7 Illustrate namespaces
  30965. /* namspace.c */
  30966.  
  30967.  
  30968. #include <stdio.h>
  30969.  
  30970. struct foo /* tag-global */
  30971. {
  30972. int foo; /* global-foo-member */
  30973. };
  30974.  
  30975. struct foo foo; /* ordinary-global
  30976. not used) */
  30977.  
  30978. main( )
  30979. {
  30980. struct foo foo; /* tag-global, ordinary-main-1 */
  30981.  
  30982. goto foo; /* label-main */
  30983.  
  30984. foo: /* label-main */
  30985.  
  30986. foo.foo = 1; /* ordinar