home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Journal 1990 - 1995 / CUJ.iso / unix / 1995.txt < prev   
Encoding:
Text File  |  1996-02-07  |  2.6 MB  |  77,685 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. Portable Byte Ordering in C++
  614.  
  615.  
  616. Philip J. Erdelsky
  617.  
  618.  
  619. Philip J. Erdelsky, Ph.D., is an R&D Engineer for Data/Ware Development, Inc.,
  620. in San Diego, California. He has been writing software in C and C++ for more
  621. than ten years. He can be reached at 75746.3411@compuserve.com.
  622.  
  623.  
  624.  
  625.  
  626. Which End is Up?
  627.  
  628.  
  629. I once facetiously asked a computer scientist where he stood on one of the
  630. greatest issues facing computing science. He must have sensed my mood, because
  631. he answered "squarely on the fence" before I could tell him what the issue
  632. was.
  633. The issue, of course, was byte order. Some CPUs, such as the Intel 80X86
  634. family, store multi-byte words in little-endian order, with the least
  635. significant byte (the little end) at the lowest address. Others, such as the
  636. Motorola 680X0 family, store them in big-endian order, with the most
  637. significant byte (the big end) at the lowest address. The terms "big endian"
  638. and "little endian" are supported by a literary allusion to Jonathan Swift's
  639. classic novel Gulliver's Travels, in which two nations fought a war to
  640. determine whether soft-boiled eggs should be opened at the big end or the
  641. little end.
  642. Each byte order has its own small advantage. Due to historical accident,
  643. numbers are written in big-endian order in English and other major western
  644. languages. That makes a memory dump easier to read if big-endian order is
  645. used. Addition, subtraction, and multiplication are done in little-endian
  646. order, so a little-endian CPU sometimes has a slight speed advantage when
  647. performing these operations in multiple precision.
  648. Could the world standardize on one byte order in the foreseeable future? It
  649. does not seem likely. The world cannot even decide which side of the road to
  650. drive on. The difference becomes a problem when little-endian and big-endian
  651. devices communicate with each other. It can be even more of a problem when
  652. their operating code has to be ported from one CPU to another with a different
  653. byte order.
  654. Writing a conversion routine is no problem. An experienced C programmer can
  655. whip one up in a minute. However, finding all the places where conversions are
  656. required can be difficult, unless the code was written with conversion in
  657. mind. That is where the techniques of C++ come in.
  658. First of all, a communication standard has to be established. If two devices
  659. communicate through a SCSI channel, all multi-byte values should be sent over
  660. the channel in big-endian order, which is the SCSI standard. Then conversions
  661. must be made to and from the CPU byte order, so the program can perform valid
  662. arithmetic operations on the data.
  663.  
  664.  
  665. The Types and Classes
  666.  
  667.  
  668. Listing 1 shows the header file endian.h, which contains nearly all the code.
  669. Listing 2 shows the file endian.cpp, which defines a useful union -- all the
  670. other code needed besides the header. The header defines three simple types,
  671. with names that are fairly standard:
  672. BYTE -- a single unsigned byte
  673. WORD -- a two-byte unsigned word
  674. DWORD -- a four-byte unsigned double word
  675. Variables of type WORD and DWORD are implicitly assumed to be in the order
  676. appropriate for the CPU, so the program can compute with them freely.
  677. The code also defines four classes of single and double words in specific byte
  678. orders:
  679. BEWORD -- a big endian WORD
  680. BEDWORD -- a big endian DWORD
  681. LEWORD -- a little endian WORD
  682. LEDWORD -- a little endian DWORD
  683. Of course, two of these types are substantially the same as WORD and DWORD,
  684. but the programmer does not need to know that while coding. The restrictions
  685. of C++ will prevent the program from performing arithmetic operations directly
  686. on them. This is important because such operations will become invalid when
  687. the program is ported to a CPU with a different byte order.
  688.  
  689.  
  690. Conversions
  691.  
  692.  
  693. Conversions from CPU order to big-endian or little-endian order are performed
  694. by a member function or by a class constructor. For example:
  695. LEWORD y(0xABCD);
  696. BEWORD x;
  697. x.set(0x1234);
  698. It is also possible to overload operator=, but this can cause problems in some
  699. implementations when unions containing these special types are initialized or
  700. assigned.
  701. Conversions from big endian or little endian order to CPU order are performed
  702. by a member function called value. For example, the following code adds 3 to a
  703. big endian WORD: 
  704. BEWORD x;
  705. x.set(x.value() + 3);
  706. An attempt to do this in a nonportable fashion will be flagged as a
  707. compile-time error: 
  708. BEWORD x;
  709. x = x + 3; // ERROR!
  710. In this case, Turbo C++ reports, "Operator cannot be applied to these operand
  711. type." 
  712. The compiler knows the byte order of the CPU on which its object code will
  713. run, but will not reveal it at preprocessing time. If the programmer has this
  714. information, the code can be made more efficient by defining either_BIG_ENDIAN
  715. or _LITTLE_ENDIAN (but not both!) to indicate the byte order to be used. For
  716. example, if _BIG_ENDIAN is defined, then x.set(0x1234), when x is of type
  717. BEWORD, will generate the code for a simple assignment.
  718. If neither _BIG_ENDIAN nor _LITTLE_ENDIAN is defined, the compiler will
  719. generate less efficient code that will work on any CPU. For example, if x is
  720. of type BEWORD, x.set(0x1234) will generate code that performs the following
  721. operations:
  722. y = 0x1234
  723. first byte of x = y >> 8
  724.  
  725. second byte of x = y
  726. If shifting is a particularly slow operation, it might be advisable to include
  727. a quick test for byte order at run time, and skip the shifting if the byte
  728. order of the CPU is the same as that of the word or double word being
  729. converted.
  730. If _RUN_TIME_ENDIAN is defined, the code will define a quick test, big_endian,
  731. which returns a true value (1) if it is executed on a big_endian CPU and a
  732. false value (0) otherwise. The code will also define a similar test called
  733. little_endian. These tests are used to skip the shifts where possible. In most
  734. implementations of C++, the tests involve no more code than testing a flag.
  735. Indeed, that is precisely how they are implemented. The initialized union
  736. _endian compiles with a 1 in either _endian.half[0] or _endian.half[1],
  737. depending on the byte order of the CPU.
  738. It is possible to overload operators, but it is generally more efficient to
  739. convert all multi-byte values to the CPU's byte order and use the regular
  740. operators. The only exceptions are operator== and operator!=, which do not
  741. depend on byte order as long as both operands use the same byte order, and
  742. tests against zero, which are implemented as the member functions zero and
  743. nonzero.
  744. All member functions have been defined inline, which makes them run fast and
  745. generates absolutely no code for member functions that are not called. If
  746. minimizing code size is desirable, it may be advisable to code some of them
  747. separately.
  748.  
  749. Listing 1 The File ENDIAN.H
  750. // Portable Byte Ordering in C++
  751. // by Philip J. Erdelsky
  752. // Public Domain -- No Restrictions on Use
  753.  
  754. // If the byte order of the target machine is known, include ONE of
  755. // the following statements:
  756. // #define _BIG_ENDIAN
  757. // #define _LITTLE_ENDIAN
  758.  
  759. // If the byte order of the target machine is to be determined at run
  760. // time for each conversion, include the following statement:
  761. // #define _RUN_TIME_ENDIAN
  762.  
  763. #ifndef _ENDIAN
  764. #define _ENDIAN 1
  765.  
  766. typedef unsigned char BYTE;
  767. typedef unsigned short WORD; // two-byte word
  768. typedef unsigned long DWORD; // four-byte double word
  769.  
  770. #ifdef _RUN_TIME_ENDIAN
  771.  
  772. extern union_endian_union
  773. {
  774. DWORD whole;
  775. WORD half[2];
  776. } _endian;
  777.  
  778. inline int big_endian(void) {return _endian.half[1];}
  779. inline int little_endian(void) {return _endian.half[0];}
  780.  
  781. #endif
  782.  
  783. // check for consistent parameter definitions
  784.  
  785. #ifdef _BIG_ENDIAN
  786. #ifdef _LITTLE_ENDIAN
  787. #error _BIG_ENDIAN and _LITTLE_ENDIAN both defined
  788. #endif
  789. #ifdef _RUN_TIME_ENDIAN
  790. #error _BIG_ENDIAN and RUN_TIME_ENDIAN both defined
  791. #endif
  792. #endif
  793.  
  794. #ifdef _LITTLE_ENDIAN
  795. #ifdef _RUN_TIME_ENDIAN
  796. #error _LITTLE_END1AN and _RUN_TIME_ENDIAN both defined
  797. #endif
  798. #endif
  799.  
  800. class BEWORD // big endian WORD
  801. {
  802.  
  803. union
  804. {
  805. BYTE half[2];
  806. WORD whole;
  807. } x;
  808. public:
  809. void set(WORD n)
  810. {
  811. #ifdef _BIG_ENDIAN
  812. x.whole = n;
  813. #else
  814. #ifdef_RUN_TIME_ENDIAN
  815. if (big_endian()) x.whole = n;
  816. else {
  817. #endif
  818. x.half[0] = n >> 8;
  819. x.half[1] = n;
  820. #ifdef _RUN_TIME_ENDIAN
  821. }
  822. #endif
  823. #endif
  824. }
  825. BEWORD(WORD n) {set(n);}
  826. WORD value(void)
  827. {
  828. return
  829. #ifdef _BIG_ENDIAN
  830. x.whole
  831. #else
  832. #ifdef RUN_TIME_ENDIAN
  833. big_endian() ? x.whole :
  834. #endif
  835. x.half[0] << 8 x.half[1]
  836. #endif
  837. ;
  838. }
  839. int zero(void) {return x.whole == 0;}
  840. int nonzero(void) {return x.whole != 0;}
  841. int operator == (BEWORD &n) {return x.whole == n.x.whole;}
  842. int operator != (BEWORD &n) {return x.whole != n.x.whole;}
  843. };
  844.  
  845. class BEDWORD // big endian DWORD
  846. {
  847. union
  848. {
  849. BYTE quarter[4];
  850. DWORD whole;
  851. } x;
  852. public:
  853. void set(DWORD n)
  854. {
  855. #ifdef _BIG_ENDIAN
  856. x.whole = n;
  857. #else
  858. #ifdef _RUN_TIME_ENDIAN
  859. if (big_endian()) x.whole = n;
  860. else{
  861. #endif
  862.  
  863. x.quarter[0] = n >> 24;
  864. x.quarter[1] = n >> 16;
  865. x.quarter[2] = n >> 8;
  866. x.quarter[3] = n;
  867. #ifdef _RUN_TIME_ENDIAN
  868. }
  869. #endif
  870. #endif
  871. }
  872. BEDWORD(DWORD n) {set(n);}
  873. DWORD value(void)
  874. {
  875. return
  876. #ifdef _BIG_ENDIAN
  877. x.whole
  878. #else
  879. #ifdef _RUN_TIME_ENDIAN
  880. big_endian() ? x.whole:
  881. #endif
  882. (DWORD) x.quarter[0] << 24 (DWORD) x.quarter[1] << 16 
  883. x.quarter[2] << 8 x.quarter[3];
  884. #endif
  885. ;
  886. }
  887. int zero(void) {return x.whole == 0;}
  888. int nonzero(void) {return x.whole != 0;}
  889. int operator == (BEDWORD &n) {return x.whole == n.x.whole;}
  890. int operator != (BEDWORD &n) {return x.whole != n.x.whole;}
  891. };
  892.  
  893. class LEWORD // little endian WORD
  894. {
  895. union
  896. {
  897. BYTE half[2];
  898. WORD whole;
  899. } x;
  900. public:
  901. void set(WORD n)
  902. {
  903. #ifdef _LITTLE_ENDIAN
  904. x.whole = n;
  905. #else
  906. #ifdef _RUN_TIME_ENDIAN
  907. if (little_endian()) x.whole = n;
  908. else {
  909. #endif
  910. x.half[1] = n >> 8;
  911. x.half[0] = n;
  912. #ifdef _RUN_TIME_ENDIAN
  913. }
  914. #endif
  915. #endif
  916. }
  917. LEWORD(WORD n) {set(n);}
  918. WORD value(void)
  919. {
  920. return
  921. #ifdef _LITTLE_ENDIAN
  922.  
  923. x.whole
  924. #else
  925. #ifdef _RUN_TIME_ENDIAN
  926. little_endian() ? x.whole :
  927. #endif
  928. x.half[1] << 8 x.half[0]
  929. #endif
  930. ;
  931. }
  932. int zero(void) {return x.whole == 0;}
  933. int nonzero(void) {return x.whole != 0;}
  934. int operator == (LEWORD &n) {return x.whole == n.x.whole;}
  935. int operator != (LEWORD &n) {return x.whole != n.x.whole;}
  936. };
  937.  
  938. class LEDWORD // little endian DWORD
  939. {
  940. union
  941. {
  942. BYTE quarter[4];
  943. DWORD whole;
  944. } x;
  945. public:
  946. void set(DWORD n)
  947. {
  948. #ifdef _LITTLE_ENDIAN
  949. x.whole = n;
  950. #else
  951. #ifdef _RUN_TIME_ENDIAN
  952. if (little_endian()) x.whole = n;
  953. else {
  954. #endif
  955. x.quarter[0] = n;
  956. x.quarter[1] = n >> 8;
  957. x.quarter[2] = n >> 16;
  958. x.quarter[3] = n >> 24;
  959. #ifdef _RUN_TIME_ENDIAN
  960. }
  961. #endif
  962. #endif
  963. }
  964. LEDWORD(DWORD n) {set(n);}
  965. DWORD value(void)
  966. {
  967. return
  968. #ifdef _LITTLE_ENDIAN
  969. x.whole
  970. #else
  971. #ifdef _RUN_TIME_ENDIAN
  972. little_endian() ? x.whole :
  973. #endif
  974. x.quarter[0] x.quarter[1] << 8 
  975. (DWORD) x.quarter[2] << 16 (DWORD) x.quarter[3] << 24;
  976. #endif
  977. ;
  978. }
  979. int zero(void) {return x.whole == 0;}
  980. int nonzero(void) {return x.whole != 0;}
  981. int operator == (LEDWORD &n) {return x.whole == n.x.whole;}
  982.  
  983. int operator != (LEDWORD &n) {return x.whole != n.x.whole;}
  984. };
  985.  
  986. #endif
  987. /* End of File */
  988.  
  989. Listing 2 The File ENDIAN.CPP
  990. #include "endian.h"
  991.  
  992. #ifdef _RUN_TIME_ENDIAN
  993.  
  994. union_endian_union_endian = {1};
  995.  
  996. #endif
  997.  
  998. // End of File
  999.  
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005.  
  1006.  
  1007.  
  1008.  
  1009.  
  1010.  
  1011.  
  1012.  
  1013.  
  1014.  
  1015.  
  1016.  
  1017.  
  1018.  
  1019.  
  1020.  
  1021.  
  1022.  
  1023.  
  1024.  
  1025.  
  1026.  
  1027.  
  1028.  
  1029.  
  1030.  
  1031.  
  1032.  
  1033.  
  1034.  
  1035.  
  1036.  
  1037.  
  1038.  
  1039.  
  1040.  
  1041.  
  1042.  
  1043.  
  1044.  
  1045.  
  1046. Transferring Numeric Values Between Computers
  1047.  
  1048.  
  1049. James A. Kuzdrall
  1050.  
  1051.  
  1052. James A. Kuzdrall has been programming digital computers since 1960 and
  1053. designing them since 1970. He is an MIT and Northeastern University graduate,
  1054. and enjoys creating efficient algorithms and using computers extensively in
  1055. engineering analysis and instrumentation control. He may be reached at Intrel
  1056. Service Co., Box 1247, Nashua, NH 03061, (603) 883-4851.
  1057.  
  1058.  
  1059.  
  1060.  
  1061. Overview
  1062.  
  1063.  
  1064. Numeric transfers between dissimilar computers can be a vexing problem for
  1065. engineers and scientists. Computers represent physical measurements by one of
  1066. several binary codes that differ in length, byte order, and sometimes format.
  1067. Direct binary data transfers between computers with different central
  1068. processors is often impossible, forcing the use of ASCII decimal (text)
  1069. equivalents.
  1070. The six functions presented here offer a fast, compact, and format-independent
  1071. alternative to ASCII transfers via printf and scanf conversions. Remarkably,
  1072. these new system-independent C functions compile without any reference to the
  1073. underlying details of the host's numeric system! You don't have to know which
  1074. binary representation the host uses. The binary transfer format (two-byte
  1075. integer, four-byte long integer, and four-byte floating point) requires 1/3.5
  1076. to 1/5.5 times less file space or transmission time than an equivalently
  1077. precise ASCII representation.
  1078. The new functions are: fputi, fgeti, fputl, fgetl, fputf, and fgetf. Each
  1079. takes two parameters, the address of the transfer data and a file (stream)
  1080. pointer. All functions return a zero if successful, a nonzero integer if not.
  1081. The functions accommodate transfers by modem, by network, by magnetic disk, or
  1082. by tape. Any transfer facility accessible through putc and getc can be used.
  1083. The transfer functions purposely avoid C library functions that may not be
  1084. supported on older K&R compilers or on stripped-down microcontroller
  1085. compilers. In fact, only putc and getc are needed.
  1086.  
  1087.  
  1088. Applications
  1089.  
  1090.  
  1091. The functions are particularly useful when transferring physical measurements
  1092. from a microcontroller-based laboratory instrument to a more powerful computer
  1093. for analysis. For example, a 68HC11-based optical radiometer might send its
  1094. 1,600-measurement scan to an 80486-class computer for a report and to a VAX
  1095. computer for inclusion in a scientific analysis. Radiometric measurements have
  1096. accuracies of only two to four digits, but span ten decades from least measure
  1097. to full scale., The floating-point functions fputf and fgetf allow the
  1098. instrument to send normal units (watts, meters) rather than risk an eventual
  1099. misinterpretation of scaled integers (microwatts-per-count,
  1100. nanometers-per-count). In addition, the faster and more precise binary
  1101. floating-point transfer requires less than one minute of 2400 baud modem time,
  1102. whereas a full precision ASCII transfer requires over five minutes.
  1103.  
  1104.  
  1105. Transfer Format
  1106.  
  1107.  
  1108. Before looking at the algorithms, consider some consequences of the transfer
  1109. format choice. Nonstandard formats, such as signed-magnitude integers and
  1110. exponent/signed-mantissa floating-point formats, are tempting choices because
  1111. they require less processing time and code in an instrument's
  1112. performance-limited microcontroller. On the other hand, the more complex but
  1113. widely used twos-complement integer and IEEE-754 floating-point formats allow
  1114. programmers to create fast, simple, system-specific versions of the
  1115. generalized transfer functions presented here in the many systems that are
  1116. known to use these formats.
  1117. Numeric range is another consideration. Simple formats often don't have the
  1118. numeric range that users expect from the C data types. For example, the
  1119. minimum signed-magnitude integer is --32,767, whereas the commonly used
  1120. twos-complement form reaches --32,768. Choosing twos-complement and IEEE-754
  1121. floating-point formats for transfer accommodates the expected range of C's
  1122. common data types, albeit just the minimum range.
  1123. Error reporting is another practical consideration. Since twos-complement
  1124. numbers use the whole numeric range of the transfer integer, the transferred
  1125. data itself cannot report an error. Instead, fputi and fputl indicate an
  1126. out-of-range or a communication error by not sending the data and returning a
  1127. nonzero integer. In the absence of some independent communication, the system
  1128. receiving data detects an error when it gets less data than expected. Although
  1129. the floating-point format has unused codes available for errors, fputf uses
  1130. the same system for consistency. The sender distinguishes transfer errors from
  1131. range errors by using ferror.
  1132.  
  1133.  
  1134. Integer Transfers
  1135.  
  1136.  
  1137. The integer transfer functions of Listing 1 take care of the byte order, the
  1138. length, and the binary codings allowed in C. The C standard stipulates that
  1139. integers use binary codes (disallowing binary-coded decimal) so that shift
  1140. operators make sense. Although twos complement is by far the most common
  1141. binary coding, the algorithm accepts signed magnitude, ones complement, and
  1142. possibly others. Table 1 compares twos complement, signed magnitude (popular
  1143. in analog-to-digital converters), and ones complement for important numbers in
  1144. the representable range.
  1145. The key to format-independent integer transfer is that all three binary
  1146. formats represent positive numbers the same way. If integers are resolved into
  1147. a positive magnitude (absolute value) and an independent sign flag, all
  1148. formats will have the same binary representation. Once the integer is
  1149. separated into sign and magnitude, C's format-independent bit-logic operations
  1150. convert it to the twos-complement transfer format.
  1151. Positive integers equal their magnitude, requiring only that the sign be
  1152. noted. Negative integers are transformed to positive magnitudes using C's
  1153. arithmetic negation -- most of the time anyway. The one exception is the most
  1154. negative twos-complement number in Table 1. It has no positive counterpart.
  1155. The value obtained for --(--32,768) is implementation-dependent and requires
  1156. special handling.
  1157. The code for fputi begins with macro definitions for MAXINT and MININT. MAXINT
  1158. is always +32,767, an informal standard for K&R compilers and a requirement
  1159. for Standard C compilers. MININT is one of two values:
  1160. --32,767 if limited by the host compiler to the minimum C requirement
  1161. --32,768 if limited by the range of the transfer integer
  1162. MININT provides a way to test for twos-complement range without using -32,768
  1163. in compilers where it is out of range.
  1164. The transfer functions use unsigned integers (lsb and msb in fputi) where one
  1165. might expect to see char variables. The longer length prevents overflow
  1166. warnings in byte arithmetic and lost bits in left shifts. Unsigned integers
  1167. also avoid the sign-fill uncertainty that occurs when using right shifts with
  1168. signed numbers. A loophole in the C standard allows either the sign bit or
  1169. zero to be put in the vacated upper bits [among other results -- pjp]. If your
  1170. compiler puts in zeros, a small negative number suddenly becomes a large
  1171. positive one! The unsigned type also prevents unwanted sign extension that
  1172. would occur with a signed char. Finally, although many K&R compilers do not
  1173. support unsigned char, all C compilers have unsigned integers.
  1174. The first if statement traps --32,768 to prevent negation errors when
  1175. obtaining the magnitude. If the number being sent is --32,768, the output
  1176. bytes are set to the correct twos-complement value. For integer values other
  1177. than --32,768, fputi splits the absolute value into two independent bytes.
  1178. ANDing with 0xff limits the unsigned integers to eight bits. The magnitudes
  1179. thus obtained are the same for any C integer format.
  1180. The magnitudes of negative numbers are restored to their signed value in the
  1181. transfer format by operations that are independent of the compiler's
  1182. representation. If *ip is negative, the algorithm negates the bytes in twos
  1183. complement by first performing a bit-wise complement then adding one.
  1184. Comparing *ip with --1 avoids problems with --0. The exclusive-OR negates only
  1185. the lowest byte, leaving the upper bytes as they are (zero), whereas C's
  1186. bit-wise negate operator would affect the entire variable.
  1187. The value at ip may have been out of range through all these operations if the
  1188. host has integers longer than 16 bits. Postponing the range check until the
  1189. end causes no processing problems because the value is masked down to bytes.
  1190. The err variable eliminates an extra return. Unstacking variables for return
  1191. consumes a significant amount of precious code space in a small
  1192. microcontroller.
  1193. The range check determines if the value held in the host's integer, possibly
  1194. four bytes long, has fit into the two transfer bytes. The only safe
  1195. conditional test for MININT is a test for equality, because a twos-complement
  1196. compiler may (erroneously) choose to negate MININT in a test for greater than.
  1197. Although no problems were encountered in testing with type int, two compilers
  1198. failed with long. Ecosoft's Eco-C Compiler Version 3.10 (1983) thought MINLNG
  1199. was greater than all other negative long values. IBM's C/C++ Version 2.0 for
  1200. OS/2 (1992) decided all positive long values were less than MINLNG. The other
  1201. compilers got it right.
  1202. putc transfers the high-order byte (msb) first to create big-endian ordering
  1203. in the file. The earlier byte masking serves another purpose when putc writes
  1204. the bytes to the file. If lsb is not masked and happens to equal EOF, putc
  1205. returns EOF whether or not there is an error.
  1206. The second function of Listing 1, fgeti, converts the transfer format of fputi
  1207. to the host's format. Since twos complement is so common, most hosts will
  1208. accept the full range of the transfer format. For those that meet only the
  1209. minimum C range, however, the reaction to --32,768 is unpredictable. If the
  1210. binary is accepted, it becomes --0 and propagates the wrong data. If the
  1211. processor traps --0 as an error, the program may stop. In both cases, an
  1212. out-of-range error from fgeti seems preferable.
  1213. After checking for transmission errors and masking, fgeti traps --32,768 if it
  1214. is beyond MININT. The first if statement test creates a compile-time constant,
  1215. TRUE or FALSE. The remaining portion always or never executes. Although the
  1216. preprocessor's #if directive would serve better here, many K&R compilers do
  1217. not support it. A code penalty is unlikely, however, because a compiler's
  1218. optimizer often eliminates code sections that logically can't execute.
  1219. Sign restoration relies on positive integers being the same in all integer
  1220. formats. If the transferred number is negative, fgeti sets the neg flag then
  1221. negates the twos-complement value, making it positive. It then assembles the
  1222. positive value byte-wise in an integer, ans. The host's arithmetic negation of
  1223. ans assures that the sign is properly installed. Again, --32,768 must be
  1224. handled separately because it has no positive counterpart. Identification is
  1225. easy, since it is the only negative integer that remains negative after
  1226. negation (0x8000+1 = 0x7fff+1 = 0x8000).
  1227. Knowing the host's format greatly simplifies both integer transfer functions.
  1228. If the host is known to use 16-bit, twos-complement, MSB-first integers, use
  1229. fwrite and fread for the transfer. If only the byte order is different
  1230. (LSB-first), use memrev before fwrite and after fread.
  1231.  
  1232.  
  1233. Long Integer Transfers
  1234.  
  1235.  
  1236.  
  1237. The long integer transfer functions fputl and fgetl, in Listing 2, extend the
  1238. integer transfer algorithm to four bytes. Like MININT, MINLNG equals either
  1239. the host compiler's limit or the transfer limit, whichever is larger (least
  1240. negative).
  1241. At first glance, it might seem that the use of fputi and fgeti could simplify
  1242. the coding. Just send the four-byte long integer as two two-byte integers.
  1243. Unfortunately, the two least-significant bytes must be sent as an unsigned
  1244. integer, and that doesn't work. Consider the long value 0x11118000. The lower
  1245. bytes, 0x8000, are out-of-range for fgeti in some hosts. On the other hand, a
  1246. host with 18-bit ones-complement integers would accept the value but produce
  1247. the wrong bit pattern.
  1248. As in the case of integers, the function code can be simplified dramatically
  1249. if the host is known to use twos-complement integers.
  1250.  
  1251.  
  1252. Floating-Point Transfers
  1253.  
  1254.  
  1255. Listing 3 shows fputf and fgetf, functions which convert the host's float
  1256. formats to and from the four-byte IEEE-754 transfer format. The IEEE float has
  1257. one sign bit, an 8-bit base-2 exponent biased by 127, and a 24-bit normalized
  1258. mantissa that ranges from 1.0 to just less than 2 (about 1.999999881). The
  1259. mantissa's always-present leading one is removed to pack the float into four
  1260. bytes. It is restored prior to arithmetic operations. Table 2 gives some
  1261. examples of floating-point values in IEEE-754 format.
  1262. If faced with manually converting decimal numbers to this format (perhaps as
  1263. punishment for the sins of one's youth), you could multiply or divide a number
  1264. by powers of two until it fell in the 1.0 to 2.0 range. A tally of these
  1265. scaling factors gives the power-of-two exponent. The mantissa is then factored
  1266. into its binary fraction bits -- with bits weighted 1.0, 0.5, 0.25, 0.125,
  1267. etc. Binary fractions, however, reach the precision limit of a ten-digit
  1268. calculator at 2 --12 , leaving the range 2 --13 to 2 --23 beyond
  1269. representation.
  1270. A better approach is to scale numbers to lie in the range 223 to 224--1. The
  1271. binary factors are now easily represented as integers, 223 being 8,388,608.
  1272. The IEEE-754 float value 0x3f800001 illustrates the practicality of the
  1273. integer-factor approach and the difficulty of entering exact binary
  1274. equivalents in decimal or of transferring them using printf. The binary
  1275. factors are easily represented, but exact decimal representation of the total
  1276. takes 24 digits.
  1277. value= (223+1)/223 * 2127-127
  1278. = (8388608+1)/8388608 * 1.0
  1279. = 1.00000011920928955078125
  1280. Interpreting the mantissa as an integer also proves useful in the float
  1281. transfer algorithm. The algorithm scales the float to the range 224--1 to 223
  1282. so that a float-to- long cast transfers all of the significant mantissa bits
  1283. to the long. Another useful trick is exponent-only arithmetic, using only
  1284. powers of two in scaling. Multiplication or division by powers of two means
  1285. addition or subtraction in the exponent, which precludes errors caused by the
  1286. finite mantissa precision. Even compilers limited to four-byte floats can
  1287. perform the scaling and cast without error.
  1288. Although the exponent of the popular IEEE-754 format represents powers of two,
  1289. the exponent of the host format could represent powers of 4, 8, 10, or 16. As
  1290. it turns out, these also convert without error when power-of-two scaling is
  1291. used. Such formats do put some bits for the power-of-two factors in the
  1292. mantissa, but the mantissa has more than enough precision to compute the
  1293. modest 2127 range of the four-byte float without error.
  1294. The precision of the compiler sets a practical limit on the powers of two used
  1295. in scaling. The compiler's strtod must convert the ASCII decimal
  1296. representation of the power-of-two constants without error. A compiler with 23
  1297. bits of float precision can convert 20 to 224 without error, but fails on 225.
  1298. The fputf code begins with a special check for 0.0, which does not respond to
  1299. scaling. The next steps take care of the sign. Two cascaded while loops scale
  1300. the number's absolute value to one. The first deals only with small numbers,
  1301. multiplying them to values greater than one in relatively large steps of 28.
  1302. This leaves the number greater than 1.0 but no more than 256.0. The test for
  1303. expo greater than zero stops the loop if the host's float is too small for the
  1304. IEEE-574 format.
  1305. The second while loop handles numbers greater than one, including the result
  1306. from the first while loop. This time all power-of-two factors present in the
  1307. number are extracted. As in the first loop, initial scaling is in large 28
  1308. increments until the number gets within range (less than 28 in this case). The
  1309. expo test stops the loop if the transfer range is exceeded. This prevents
  1310. lockup if *np was the IEEE-574 representation of infinity, which, like zero,
  1311. does not scale.
  1312. The two loops produce both the exponent and, upon multiplication by 223, the
  1313. long integer mantissa. The encoding concludes with a byte-wise assembly of the
  1314. IEEE-754 float in an array of characters. The range check uses the exponent
  1315. rather than a maximum and minimum float value to avoid compiler inaccuracies
  1316. in the ASCII-to-binary conversion. The putc loop sends the array (or zero) in
  1317. MSB-first order.
  1318. The choice of 28 as the maximum scale factor minimizes conversion time.
  1319. Scaling by 2.0 could be used, of course, but it would require 127 divisions to
  1320. reduce 2127 to 20. The largest allowed constant, 223, hits a maximum when *np
  1321. is 2114, requiring four divisions by 223 plus 22 sub-power scalings for a
  1322. total of 26 divisions. By contrast, 28 scales 2127 down to 20 in 22 divisions.
  1323. As it turns out, the optimum maximum scale factor is the square root of the
  1324. exponent. The choice of 28 optimizes conversions in the mid-range of exponents
  1325. (2*1019 to 2*10-19 ) where most physical measurements fall.
  1326. Moving on to fgetf, four bytes are checked for transmission errors as they are
  1327. read into an array of unsigned integers, byt. Taking advantage of the loop set
  1328. up for reading, they are also masked to eight bits and checked for zero. After
  1329. unpacking the bytes to get expo and mant, a range check of expo intercepts
  1330. erroneous or unintended values. Zero is also handled here since it won't
  1331. scale.
  1332. The multiply and divide loops build the float exponent factor by error-free
  1333. power-of-two arithmetic. The loops scale quickly by whole 28 factors, then
  1334. finish the remainder (27 to 20) in a single arithmetic operation. The variable
  1335. pwr contains 2expo-127 when done. The separate treatment based on the initial
  1336. expo value prevents underflow or overflow in four-byte float hosts.
  1337. The error check detects most but not all host overflow problems. In hosts
  1338. using four-byte float with an exponent bias of 128, for example, pwr usually
  1339. denormalizes and/or reaches zero for numbers in the highest octave of the
  1340. IEEE-754 range. The rest of the float range transfers accurately.
  1341. If pwr seems valid, the code negates it if the original sign bit was set. An
  1342. intermediate variable, ftemp, prevents underflow by controlling the order of
  1343. arithmetic operations.
  1344.  
  1345.  
  1346. Testing
  1347.  
  1348.  
  1349. Two programs are included on this month's code disk that aren't listed here.
  1350. The first is a program to generate a file of test numbers. The second is a
  1351. program to read the test number file and test the transfer routines. The
  1352. transfer functions send data that will be the same in all computers that
  1353. receive it. Be aware when testing, however, that the float data sent may not
  1354. exactly match the originator's data due to rounding or truncation. Instead,
  1355. the originator must process its data through fputf and fgetf, perhaps using a
  1356. file, to have the same values as the other systems.
  1357. Assume two systems, A and B, must share data originating in A. For simplicity,
  1358. assume they can exchange data on a common floppy-disk format. To test the
  1359. functions, system A writes a range of data to a binary file using fputi,
  1360. fputl, and fputf. System B reads the file using fgeti, fgetl, and fgetf. From
  1361. its internal data, system B creates a second file using the fput functions. It
  1362. should be the duplicate of the one B just read. System A reads both files with
  1363. fget functions, comparing them item by item.
  1364. Neither system should depend on a file-matching utility to compare the files,
  1365. because A and B may use different fill characters in the unused space at the
  1366. end of the file.
  1367. Table 1 Integer Formats
  1368. Decimal 2's Comp Sign-Mag 1's Comp
  1369. -------------------------------------
  1370. +32767 0x7fff 0x7fff 0x7fff
  1371. +1 0x0001 0x0001 0x0001
  1372. +0 0x0000 0x0000 0x0000
  1373. -0 none 0x8000 0xffff
  1374. -1 0xffff 0x8001 0xfffe
  1375. -32767 0x8001 0xffff 0x8000
  1376. -32768 0x8000 none none
  1377. Table 2 IEEE-754 Floats
  1378. Decimal IEEE-754 Sign Expo Mantissa
  1379. -------------------------------------------------
  1380. +largest 7f7fffff 0 fe ffffff (about
  1381.  3.402822e+38)
  1382. +32769.00 47000100 0 8e 800100
  1383. +32768.00 47000000 0 8e 800000
  1384. +32767.50 46ffff00 0 8d ffff00
  1385. +32767.00 46fffe00 0 8d fffe00
  1386. +1.000000 3f800000 0 7f 800000
  1387. -1.000000 bf800000 1 7f 800000
  1388. +smallest 00800000 0 01 800000 (about
  1389.  1.17549e-38)
  1390. +0.000000 00000000 0 00 800000 (zero by
  1391.  
  1392.  definition)
  1393.  
  1394. Listing 1 Integer Transfer Functions
  1395. /* Copyright (C) 1994 James A. Kuzdrall
  1396. Free license to this software is granted only if the above
  1397. copyright credit is included in the source code */
  1398.  
  1399. /* ***< IHTRXF.CEE >***
  1400.  
  1401. /* put system headers here */
  1402. #include <stdio.h> /* usual definitions */
  1403.  
  1404. /* use limit if does not exceed range of host machine */
  1405. #define MAXINT 32767 /* limit +32767 */
  1406. #define MININT -32768 /* limit -32768 */
  1407. #define XFER_ERROR -1 /* a non-zero integer */
  1408.  
  1409. /***< fputi >*** ++++++++++++++++++++++++++++++++++++++++++++++ *
  1410.  
  1411. USE ...... Convert host's integer to the 2-byte, 2's complement,
  1412. MSB-first standard transfer format. Send the two bytes to the
  1413. output stream fp.
  1414. RETURNS... Non-zero if error; otherwise, 0
  1415. No number sent if error.
  1416. ERRORS.... Disk errors of putc().
  1417. Overrange for integers beyond transfer range, 32767 to -32768,
  1418. */
  1419.  
  1420. int fputi(ip,fp)
  1421. int *ip; /* integer to put on stream */
  1422. FILE *fp; /* stream (file) pointer */
  1423. {
  1424. int absi; /* absolute value to transfer */
  1425. unsigned int msb; /* build 2's complement here */
  1426. unsigned int lsb;
  1427. int err;
  1428.  
  1429. /* detect special case, -32768 */
  1430. if( MININT != -32767 && *ip == MININT ) {
  1431. lsb= 0: /* set 2's complement -32768 */
  1432. msb= 0x80;
  1433. }
  1434. else {
  1435. /* avoid problem with -MININT in 2's comp */
  1436. absi= (*ip <= -1 && *ip >= -32767) ? -*ip: *ip;
  1437.  
  1438. /* divide magnitude into bytes */
  1439. lsb= absi & 0xff; /* mask to one byte */
  1440. msb= (absi >> 8) & 0xff;
  1441.  
  1442. /* negate bytes in 2's comp if number is negative */
  1443. if( *ip <= -1 ) { /* less than -0 */
  1444. lsb= (lsb ^ 0xff) +1;
  1445. msb= (msb ^ 0xff) + (lsb >> 8); /* add carry, if any */
  1446. }
  1447. }
  1448.  
  1449. err= XFER_ERROR; /* preset to error */
  1450. /* if in range, send MSB first */
  1451.  
  1452. if( *ip <= MAXINT && (*ip >= -32767 *ip == MIHINT) &&
  1453. putc(msb,fp) != EOF && putc(lsb,fp) != EOF )
  1454. err= 0;
  1455.  
  1456. return( err ):
  1457. }
  1458.  
  1459. /***< fgeti >*** ++++++++++++++++++++++++++++++++++++++++++++++ *
  1460.  
  1461. USE ....... Read a standard format, 2-byte, 2's complement, MSB-
  1462. first integer from the stream fp and convert it to the host's
  1463. integer format.
  1464. RETURNS... Non-zero if error; otherwise, 0
  1465. Places answer at pointer if no errors.
  1466. ERRORS.... From getc() only; generates no errors of its own.
  1467. */
  1468.  
  1469. int fgeti(ip,fp)
  1470. int *ip; /* place where answer goes */
  1471. FILE *fp; /* file pointer given by fopen() */
  1472. {
  1473. unsigned int msb,lsb; /* might be EOF */
  1474. int ans;
  1475. char neg; /* non-zero (true) if original nr was negative */
  1476. int err;
  1477.  
  1478. err= XFER_ERROR; /* preset */
  1479. /* transmission errors */
  1480. if( (msb= getc(fp)) == EOF (lsb= getc(fp)) == EOF )
  1481. goto gixit; /* data error */
  1482.  
  1483. msb &= 0xff; /* remove unwanted high bits, if any */
  1484. lsb &= 0xff;
  1485.  
  1486. /* detect special case, -32768 in non-2's complement host */
  1487. if( MININT == -32767 && msb == 0x80 && !lsb )
  1488. goto gixit; /* out-of-range error */
  1489. err= 0; /* no other errors possible */
  1490.  
  1491. /* use 2's comp negate to make negative nors positive; save sign */
  1492. if( (neg= (msb & 0x80)) ) {
  1493. lsb= ((lsb & 0xff) ^ 0xff) +1; /* perhaps produces carry bit */
  1494. msb =(((msb & 0xff) ^ 0xff) + (lsb >> 8)) & 0xff;
  1495. }
  1496.  
  1497. /* cascade bytes to form positive int, then correct the sign */
  1498. if( msb & 0x80 ) /* only MININT is still neg */
  1499. *ip= MININT;
  1500. else { /* for all but MININT */
  1501. ans= (msb << 8) + (lsb & 0xff);
  1502. *ip = neg ? -ans : ans; /* let host install sign */
  1503. }
  1504.  
  1505. gixit:
  1506. return( err );
  1507. }
  1508.  
  1509. /* End of File */
  1510.  
  1511.  
  1512.  
  1513. Listing 2 Long Transfer Functions
  1514. /* Copyright (C) 1994 James A. Kuzdrall
  1515. Free license to this software is granted only if the above
  1516. copyright credit is included in the source code */
  1517.  
  1518. /* ***< LNGTXF.CEE >***
  1519.  
  1520. /* put system headers here */
  1521. #include <stdio.h> /* usual definitions */
  1522.  
  1523. /* use limit if does not exceed range of host machine */
  1524. #define MAXLNG 2147483647L /* limit +2147483647 */
  1525. #define MINLNG -2147483648L /* limit -2147483648 */
  1526. #define XFER_ERROR -1 /* a non-zero integer */
  1527.  
  1528. /***< fputl >*** ++++++++++++++++++++++++++++++++++++++++++++++ *
  1529.  
  1530. USE....... Convert host's long integer to the 4-byte, 2's comple-
  1531. ment, MSB-first standard transfer format. Send the four bytes
  1532. to the output stream fp.
  1533. RETURNS... Non-zero if error; otherwise, 0
  1534. No number sent if error.
  1535. ERRORS.... Disk errors of putc().
  1536. Overrange if exceeds transfer range, 2147483647 to -2147483648.
  1537. */
  1538.  
  1539. int fputl(lp,fp)
  1540. long *lp; /* long to put on disk */
  1541. FILE *fp; /* file pointer given by fopen() */
  1542. {
  1543. int ix; /* byte counter */
  1544. long absl: /* absolute value of *lp */
  1545. unsigned int byt[5]; /* byt[0] is most significant */
  1546. int err;
  1547.  
  1548. if( MINLNG != -2147483647 && *lp == MINLNG ) {
  1549. byt[0]= 0x80; /* set 2's complement -2147483648 */
  1550. byt[1]=byt[2]=byt[3]=0
  1551. }
  1552. else {
  1553. /* avoid problem with -MINLNG in 2's comp, 4-byte */
  1554. absl= (*lp <= -1L && *lp >= -2147483647) ? -*lp : *lp;
  1555.  
  1556. /* divide magnitude into bytes; if neg, negate in 2's comp */
  1557. byt[4]= 0x100; /* becomes 1 after >> 8 */
  1558. for( ix= 3; ix >= 0; ix-- ) { /* process from LSB to MSB */
  1559. byt[ix]= (absl >> (8*(3-ix))) & 0xff;
  1560. if( *lp <= -1L )
  1561. byt[ix]= (byt[ix] ^ 0xff) + (byt[ix+1] >> 8);
  1562. }
  1563. }
  1564. err= XFER_ERROR; /* preset */
  1565. /* if in range, send MSB first */
  1566. if( *lp <= MAXLNG && (*lp >= -2147483647 *lp == MINLNG) ) {
  1567. for( ix= 0; ix < 4; ix++ )
  1568. if( putc(byt[ix],fp) == EOF )
  1569. goto plxit;
  1570. err=0;
  1571.  
  1572. }
  1573. plxit:
  1574. return( err );
  1575. }
  1576.  
  1577. /***< fgetl >*** +++++++++++++++++++++++++++++++++++++++++++++++*
  1578.  
  1579. USE....... Read a standard format, 4-byte long integer from the
  1580. input stream fp and convert it to the host's long format.
  1581. RETURNS... ERROR if error; otherwise, 0
  1582. Places answer at pointer if no errors.
  1583. ERRORS.... From getc() only; generates no errors of its own.
  1584. */
  1585.  
  1586. int fgetl(lp,fp)
  1587. long *lp; /* place where answer goes */
  1588. FILE *fp; /* file pointer given by fopen() */
  1589. {
  1590. int ix; /* byte counter */
  1591. char neg; /* non-zero (true) if original nr was negative */
  1592. long ans; /* temporary */
  1593. unsigned int byt[5]; /* byt[0] is most significant */
  1594. int err;
  1595.  
  1596. err: XFER_ERROR; /* preset */
  1597. for( ix= 0; ix<4; ix++ ) { /* byte read from MSB to LSB */
  1598. if( (byt[ix]= getc(fp)) == EOF )
  1599. goto glxit;
  1600. byt[ix] &= 0xff; /* remove any high bits */
  1601. }
  1602.  
  1603. /* detect special case, -2147483648 in non-2's complement host */
  1604. if( MINLNG == -2147483647 && byt[0] == 0x80 && !byt[1] &&
  1605. !byt[2] && !byt[3] )
  1606. goto glxit; /* out-of-range error */
  1607. err= 0; /* no error */
  1608.  
  1609. ans= 0L;
  1610. byt[4]= 0x100; /* becomes 1 after >> 8 */
  1611. neg= byt[0] & 0x80;
  1612. for( ix= 3; ix >= 0; ix-- ) {
  1613. if( neg ) /* make negative nrs positive by 2's comp negate */
  1614. byt[ix]= (byt[ix] ^ 0xff) + (byt[ix+1] >> 8);
  1615. ans = ((long)(byt[ix] & 0xff)) << 8*(3-ix);
  1616. }
  1617.  
  1618. /* handle MINLNG separately; has negate and range problems */
  1619. if( byt[0] & 0x80 ) /* only MINLNG is still neg */
  1620. *lp= MINLNG;
  1621. else /* for all but MINLNG /*
  1622. *lp= neg ? -ans : ans; /* let host install sign */
  1623.  
  1624. glxit:
  1625. return( err );
  1626. }
  1627. /* End of File */
  1628.  
  1629.  
  1630. Listing 3 Floating-Point Transfer Functions
  1631.  
  1632. /* Copyright (C) 1994 James A. Kuzdrall
  1633. Free license to this software is granted only if the above
  1634. copyright credit is included in the source code */
  1635.  
  1636. /* ***< FLTTXF.CEE >***
  1637.  
  1638. #include <stdio.h> /* ANSI C definitions */
  1639. #define XFER_ERROR -1 /* a non-zero integer */
  1640.  
  1641. static float pwr2[9]= {
  1642. 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0 };
  1643.  
  1644. /***< fputf >*** ++++++++++++++++++++++++++++++++++++++++++++++*
  1645.  
  1646. USE....... Convert host's float to a 4-byte, IEEE-754, MSB-first
  1647. standard transfer format. Send the 4 bytes to output stream fp.
  1648. RETURNS... Non-zero if error; otherwise, 0
  1649. No number sent if error.
  1650. ERRORS.... Disk errors of putc().
  1651. Out-of-range for absolute values beyond IEEE 4-byte float range,
  1652. about 3.402822e+38 to 1.175493e-38.
  1653. */
  1654.  
  1655. int fputf(np,fp)
  1656. float *np; /* number to be sent */
  1657. FILE *fp; /* stream (file) pointer */
  1658. {
  1659. float nr; /* local copy of number */
  1660. char iszero; /* flag: 1 if nr is 0.0, else 0 */
  1661. int expo; /* build exponent here */
  1662. int ix; /* index for powers of two */
  1663. long mant; /* mantissa */
  1664. unsigned int byt[4]; /* build IEEE float here */
  1665. int err;
  1666.  
  1667. byt[0]= iszero= 0; /* preset float sign + and not zero */
  1668. expo= 127; /* preset exponent bias at 2^0 */
  1669. if( (nr= *np) == 0.0 ) { /* zero won't scale; just send it */
  1670. iszero= 1;
  1671. goto pfout; /* note: preset expo passes pfout test */
  1672. }
  1673.  
  1674. /* get magnitude; put sign bit output byte */
  1675. if( nr < 0.0 ) {
  1676. byt[0]= 0x80;
  1677. nr= -nr;
  1678. }
  1679.  
  1680. /* scale nr to 2^0 to 2^1 range; build exponent by powers of 2 */
  1681. while( nr < 1.0 && expo > 0 ) { /* make small numbers > 1.0 */
  1682. nr *= 256.0;
  1683. expo -= 8;
  1684. }
  1685. for( ix= 8; --ix; ) {
  1686. while( nr >= pwr2[ix] && expo < 255 ) {
  1687. nr /= pwr2[ix);
  1688. expo += ix;
  1689. }
  1690. }
  1691.  
  1692.  
  1693. /* scale nr to 2^24 to 2^23 range; convert to long integer */
  1694. mant= (long )( nr * 8388608.0 ); /* 2^23 */
  1695. /* get mantissa bytes from the long */
  1696. byt[3]= mant;
  1697. byt[2]= (mant >> 8);
  1698. byt[1]= (mant >> 16);
  1699.  
  1700. /* encode exponent */
  1701. if( expo & 0x0001 ) /* put lsb of expo in implied bit */
  1702. byt[1] = 0x80;
  1703. else
  1704. byt[1] &= 0x7f;
  1705. /* combine rest of exponent and sign */
  1706. byt[0] = (unsigned int )expo >> 1;
  1707.  
  1708. pfout:
  1709. err= XFER_ERROR; /* preset */
  1710. /* look for out-of-range exponent and write errors */
  1711. if( expo < 255 && expo > 0 ) {
  1712. for( ix= 0; ix < 4; ix++ )
  1713. if( putc( (iszero ? 0 : (byt[ix] & 0xff)),fp) == EOF )
  1714. goto pfxit;
  1715. err= 0;
  1716. }
  1717. pfxit:
  1718. return( err );
  1719. }
  1720.  
  1721. /***< fgetf >*** +++++++++++++++++++++++++++++++++++++++++ *
  1722.  
  1723. USE....... Read a standard transfer format, 4-byte, IEEE-754, MSB-
  1724. first float from the stream fp and convert it to the host's
  1725. float format.
  1726. RETURNS... Non-zero if error; otherwise, 0
  1727. Places answer at pointer if no errors.
  1728. ERRORS.... From getc().
  1729. Bad exponent byte (255).
  1730. */
  1731.  
  1732. int fgetf(np,fp)
  1733. float *np; /* place where answer goes */
  1734. FILE *fp; /* stream (file) pointer*/
  1735. {
  1736. int ix; /* byte counter */
  1737. char nonzero; /* true if any bits set */
  1738. int expo; /* exponent of IEEE float */
  1739. long mant; /* fractional part */
  1740. float pwr; /* scale factor from exponent */
  1741. float ftemp;
  1742. unsigned int byt[4]; /* transferred bytes, byt[0] is MSB */
  1743. int err;
  1744.  
  1745. /* read bytes from disk; check error */
  1746. err= XFER_ERROR; /* preset true */
  1747. nonzero= 0; /* preset false */
  1748. for( ix= 0; ix<4; ix++ ) { /* byte read from MSB to LSB */
  1749. if( (byt[ix]= getc(fp)) == EOF )
  1750. goto gfxit; /* disk error, quit */
  1751.  
  1752. byt[ix] &= 0xff; /* strip parity etc, if present */
  1753. if( byt[ix] ) /* see if any bits set */
  1754. nonzero++;
  1755. }
  1756.  
  1757. /* divide IEEE-754 float into parts */
  1758. expo= ((byt[0] << 1) (byt[1] >> 7)) & 0xff;
  1759. mant= ( ( (long )(byt[1] 0x88) << 16) /* implied bit */
  1760. + (byt[2] << 8) + byt[3] );
  1761.  
  1762. /* intercept IEEE-754 Inf, Nan, denormalized or just bad byte */
  1763. if( expo == 0xff (!expo && nonzero) )
  1764. goto gfxit;
  1765.  
  1766. /* special case: number is zero */
  1767. if( !nonzero ) {
  1768. *np= 0.0;
  1769. err= 0;
  1770. goto gfxit;
  1771. }
  1772.  
  1773. /* build float value of exponent in pwr */
  1774. pwr= 1.0; /* 2^0 scaling multiplier corresponds to expo=127 */
  1775. if( expo > 127 ) {
  1776. while( expo > 135 ) {
  1777. pwr *= 256.0;
  1778. expo -= 8;
  1779. }
  1780. pwr *= pwr2[expo-127];
  1781. }
  1782. else {
  1783. while( expo < 119 ) {
  1784. pwr *= .00390625; /* equiv /= 256.0 */
  1785. expo += 8;
  1786. }
  1787. pwr /= pwr2[127-expo];
  1788. }
  1789.  
  1790. /* check for over and under flow */
  1791. if( pwr > 0.0 ) {
  1792. if( byt[0] & 0x80 )
  1793. pwr= -pwr; /* set right sign */
  1794. /* convert mantissa to 1.0 to 2.0 range then scale by pwr */
  1795. ftemp= (float )mant/8388608.0; /* mant/2^23 */
  1796. *np= ftemp * pwr; /* separate lines prevent mant*pwr overflow */
  1797. err= 0;
  1798. }
  1799. gfxit:
  1800. return( err );
  1801. }
  1802.  
  1803. /* End of File */
  1804.  
  1805.  
  1806.  
  1807.  
  1808.  
  1809.  
  1810.  
  1811.  
  1812.  
  1813.  
  1814.  
  1815.  
  1816.  
  1817.  
  1818.  
  1819.  
  1820.  
  1821.  
  1822.  
  1823.  
  1824.  
  1825.  
  1826.  
  1827.  
  1828.  
  1829.  
  1830.  
  1831.  
  1832.  
  1833.  
  1834.  
  1835.  
  1836.  
  1837.  
  1838.  
  1839.  
  1840.  
  1841.  
  1842.  
  1843.  
  1844.  
  1845.  
  1846.  
  1847.  
  1848.  
  1849.  
  1850.  
  1851.  
  1852.  
  1853.  
  1854.  
  1855.  
  1856.  
  1857.  
  1858.  
  1859.  
  1860.  
  1861.  
  1862.  
  1863.  
  1864.  
  1865.  
  1866.  
  1867.  
  1868.  
  1869.  
  1870.  
  1871.  
  1872.  
  1873.  
  1874.  
  1875. Linux - The Low Cost UNIX
  1876.  
  1877.  
  1878. Rick Roberts
  1879.  
  1880.  
  1881. Rick Roberts is president of Wintech Solutions, a technical software
  1882. consulting company in Arlington, Texas, specializing in user friendly custom
  1883. technical software and embedded system design. He can be reached via email at
  1884. ricrob@metronet.com or by phone at (817) 860-7591.
  1885.  
  1886.  
  1887.  
  1888.  
  1889. What is Linux?
  1890.  
  1891.  
  1892. UNIX for free? Yes, if you have access to the Internet, you can obtain a fully
  1893. functional, 32-bit, UNIX operating system for nothing. If you don't have
  1894. access to the Internet, there are several other ways of acquiring it, with the
  1895. cost depending on the method you choose, as I explain later.
  1896. This UNIX clone, called Linux, is designed specifically for 386/486 ISA-bus
  1897. machines. Linux includes the following features: X-Window (based on the
  1898. Xfree86 X server), the GNU C compiler, the GNU shell Bash, man pages, support
  1899. for up to four virtual terminals, network and mail support, and all of the
  1900. standard UNIX utilities. Hardware supported includes AT-type MFM, RLL, ESDI,
  1901. and IDE hard disks, high density 3.5 and 5.25 inch floppy drives, SCSI, serial
  1902. and bus mice, and several VGA cards at up to 1028x768x256 resolution. A
  1903. typical Linux distribution will also include some application programs as
  1904. well, such as spreadsheets, TeX, various games, communication programs, and
  1905. others.
  1906. Linux is POSIX-compliant and implements a subset of System V, so it's usually
  1907. very easy to port applications written for other UNIX systems to run under
  1908. Linux. For instance, I have compiled and run some of the X-Window code
  1909. examples from the O'Reilly books without any modifications. You may even be
  1910. able to use some commercial applications if they are distributed with source
  1911. code so that you can compile them under Linux.
  1912. Linux (the 'i' is pronounced as the 'i' in UNIX) was created by Linus
  1913. Torvalds. It began as a simple task switcher running two processes, which
  1914. printed "AAAA" and "BBBB." Gradually, the task switcher grew until it evolved
  1915. into a full UNIX-compatible kernel. Over the years, many people on the
  1916. Internet have contributed code to make it what it is today. Linux is not
  1917. public domain; it is copyrighted by Linus Torvalds. Fortunately, Torvalds'
  1918. copyright is the same as the GNU General Public Licence, a relatively liberal
  1919. set of restrictions that actually promote the free distribution of software
  1920. (see the sidebar, "The Linux Copyright"). Since Linux was totally written from
  1921. scratch, all of its source code can be distributed without royalties.
  1922.  
  1923.  
  1924. System Requirements
  1925.  
  1926.  
  1927. You will need at least 4 Mb of memory to run Linux. After that, your memory
  1928. requirements will vary, depending upon what applications you use and upon your
  1929. patience: while you may get by on 4 MB (Linux implements virtual memory), some
  1930. tasks will cause your machine to run noticeably slower, as Linux constantly
  1931. swaps to disk when you run out of memory. Two such applications are the C
  1932. compiler and X-Window. Your hard disk should have a minimum of 40 MB free, 65
  1933. MB if you want X-Window. You will probably want to set aside at least an extra
  1934. 10 MB for user files.
  1935.  
  1936.  
  1937. Getting Linux from the Internet
  1938.  
  1939.  
  1940. Linux is available via anonymous ftp at several Internet sites. Some of these
  1941. are shown in Table 1. Not all sites carry exactly the same distribution. As
  1942. Linux is a product of the Internet, it has grown through the contributions of
  1943. many different people; so you will find several different flavors of it on the
  1944. net. Usually, the difference in flavor will be in the kernEl's revision
  1945. number; you can expect different flavors to have different functionality.
  1946. Other variances occur in the hardware supported (for example, one version
  1947. supports Sound Blaster cards), enhanced features and code improvements, which
  1948. bugs got fixed, application programs included, and the installation procedure.
  1949. A typical Linux distribution includes the binaries (compiled executables), so
  1950. all you need to do is follow the installation procedure and the directory
  1951. structure will be setup for you automatically. You will be ready to run Linux
  1952. without having to compile it. Two of my favorite Linux distributions are
  1953. SlackWare's and Texas A&M University's (TAMU). Both have an easy installation
  1954. procedure and come with lots of auxiliary programs. The following session log
  1955. shows a user connecting to the TAMU site and getting one of the disk images:
  1956. bash$ ftp net.tamu.edu
  1957. Connected to net.tamu.edu.
  1958. 220 saturn FTP server (Version wu-2.1c(5) Fri Jan 14
  1959. 16:46:43 CST 1994) ready.
  1960. Name (net.tamu.edu:ricrob): anonymous
  1961. 331 Guest login ok, send your complete e-mail address as
  1962. password.
  1963. Password:
  1964. Remote system type is UNIX.
  1965. Using binary mode to transfer files.
  1966. ftp> cd /pub/linux/TAMU.99p12/image.3inch
  1967.  
  1968. 250 CWD command successful.
  1969. ftp> binary
  1970. 200 Type set to I.
  1971. ftp> get bin.01
  1972. 200 PORT command successful.
  1973. 150 Opening BINARY mode data connection for bin.01
  1974. (1474560 bytes).
  1975.  
  1976. 226 Transfer complete.
  1977. 1474560 bytes received in 80.33 seconds (17.93 Kbytes/s)
  1978. ftp> quit
  1979. 221 Goodbye.
  1980.  
  1981.  
  1982.  
  1983. Installing Linux
  1984.  
  1985.  
  1986. Once you have downloaded all the disk images, you must place them on
  1987. individual floppy disks. Some sites use MS-DOS compatible files while others
  1988. use Linux-format files. Even if the site uses DOS-format disks, you will still
  1989. need a Linux formatted installation boot disk (this is usually the disk made
  1990. from the file a1). You can prepare this disk using a raw disk writing utility,
  1991. such as rawrite.exe; or if you already have Linux running on another machine,
  1992. you can use that machine to make up the disks using the UNIX dd command. You
  1993. can get rawrite.exe from wcarchive.cdrom.com in the pub/linux/old_slackware
  1994. directory.
  1995. After making up the set of installation floppies, boot your computer using the
  1996. installation boot disk. During the boot process, the Linux kernel will be
  1997. loaded into memory. If all goes well during the boot, you should see a login
  1998. prompt. At this point Linux is running and you will need to be familiar with
  1999. UNIX commands to proceed. Login as root and search the directory for read.me
  2000. files that will give you information on the installation process. Also, be
  2001. sure to read any read.me files and the FAQ (frequently asked questions) file
  2002. from the distribution site.
  2003. The first thing you must do before going any further is create a partition on
  2004. your hard disk for Linux. Creating a Linux partition will destroy all data on
  2005. your hard disk, so you may as well start with an empty hard disk. To create
  2006. the partition, use the fdisk program from the boot disk. After fdisk, you will
  2007. need to reboot. At this point, it is a good idea to have an understanding of
  2008. UNIX system administration. UNIX is a much more complicated and unforgiving
  2009. operating system than MSDOS. UNIX uses disk caches which must be flushed prior
  2010. to rebooting. Before you reboot, run the sync command a couple of times to
  2011. write the cache to disk. Also, if halt is on the boot floppy, run that after
  2012. the sync command. halt prepares the operating system for a power off.
  2013. After rebooting, run the installation shell script. It probably will be named
  2014. something like setup. Simply follow all the prompts and that's all there is to
  2015. it.
  2016. One nice thing about the TAMU distribution is that it will install LILO (LInux
  2017. LOader), the disk boot loader program. This program enables you to boot Linux
  2018. from a hard disk instead of a floppy. As your computer boots up, LILO will ask
  2019. you which partition you wish to boot from, which allows you to boot either DOS
  2020. or Linux. If you don't select anything LILO times out and boots from your
  2021. first partition. TAMU also includes a menu system that makes it easy to do
  2022. things like format disks and setup new users.
  2023. Although the TAMU release has a very straightforward installation procedure, I
  2024. found some peculiarities as well. The TAMU release is supposed to work with 4
  2025. MB memory, but I could not get the installation to complete successfully with
  2026. only 4 MB. The installation would fail and display an "out of memory" message
  2027. during the mkefs part of the installation. When I added more memory (16 more
  2028. MB), I was able to get through the file system creation but bombed out while
  2029. loading from the second installation disk. This second blowup, I learned, was
  2030. probably due to a peculiarity with my computer. Evidently, I had installed too
  2031. much memory. I dropped down to 8 MB and had no more problems. Once Linux was
  2032. installed, it was able to run with 4 MB of memory, but as it was booting up,
  2033. an out-of-memory message was displayed. After that I ran the system with 4 MB
  2034. for a while, and didn't seem to have any difficulties, even while running
  2035. X-Window. However, the system was constantly swapping out memory to the hard
  2036. disk and ran very slow, so I would be wary of running the system with only 4
  2037. MB.
  2038.  
  2039.  
  2040. Using Linux
  2041.  
  2042.  
  2043. Once you have successfully installed Linux and are up and running, find and
  2044. read all of the read.me files and FAQs. You should add a new user account as
  2045. soon as possible and login with it, so you don't have to login as root with
  2046. superuser privileges. Since you will be the system administrator, I suggest
  2047. you get a good book on system administration such as the UNIX System
  2048. Administration Handbook by Nemeth, Snyder, and Seebass. [1]
  2049. Linux provides a wonderful opportunity for those wanting to learn about UNIX
  2050. or operating systems in general. Because the source code is available you can
  2051. learn all the ins and outs of operating system design -- you can even add your
  2052. own embellishments. If you come up with something really useful, you can
  2053. submit it as a contribution for the next release. You can also learn a lot
  2054. about UNIX system administration. Linux will allow your 386/486 to do true
  2055. multitasking with multiuser capabilities, and since it is POSIX compatible,
  2056. you will be able to run lots of the free UNIX software available on the
  2057. Internet.
  2058. If you can connect to the Internet, a newsgroup named comp.os.linux devoted to
  2059. Linux can provide current information about it. Another useful newsgroup for
  2060. general UNIX questions -- such as how to create a directory, etc. -- is
  2061. comp.unix.questions.
  2062.  
  2063.  
  2064. Other Linux Sources
  2065.  
  2066.  
  2067. If you cannot access the Internet, you can obtain Linux from several
  2068. commercial sources. Some companies sell Linux already installed and runnable
  2069. on a CDROM. Table 2 lists several commercial sources of Linux. Also, several
  2070. computer magazines have advertisements for companies selling Linux. As
  2071. mentioned before, because Linux is a product of the Internet and is in a
  2072. constant state of flux, different sources will carry different versions and
  2073. kernel patch levels. So before spending any money be sure to find out if their
  2074. version is compatible with your hardware.
  2075.  
  2076.  
  2077. Bibliography
  2078.  
  2079.  
  2080. [1] Nemeth, Snyder, and Seebas. Unix System Administration Handbook. Prentice
  2081. Hall, ISBN 0-13-933441-6.
  2082. The Linux Copyright
  2083. How is Linux copyrighted and how does this concern you? That depends on how
  2084. you intend to use Linux. If, like most people, you will just be an end user,
  2085. you probably need to read no further. What if you want to use Linux as a
  2086. software development platform? That's still probably not a problem, unless you
  2087. develop software that specifically uses Linux and is intended for outside
  2088. distribution. Here things start to get a little murky. There's a difference
  2089. between just making system calls (which is okay), and incorporating Linux code
  2090. as part of your own. If you intend to do the latter, you'll have to meet a few
  2091. requirements, as described in the rest of this sidebar. The same is true if
  2092. you intend to redistribute Linux, or any modified portion thereof.
  2093. The Linux copyright applies the same restrictions as another, well-known
  2094. copyright, the GNU General Public License (GPL), a.k.a. the GNU CopyLeft. The
  2095. GPL is a software license offered by the Free Software Foundation, designed to
  2096. promote rather than discourage the free distribution and use of software. The
  2097. GPL achieves this by placing certain requirements upon persons who distribute
  2098. the licensed software.
  2099. At the heart of the GPL are three requirements: 1) to make source code
  2100. available along with any GPL-licensed software that is distributed; 2) to
  2101. include a disclaimer of warranty; 3) to propagate the GPL license with the
  2102. distribution.
  2103. The first requirement basically states that anyone who distributes software
  2104. covered by the GPL must be prepared to supply the accompanying source code to
  2105. users who request it. The same requirement applies to anyone who modifies and
  2106. distributes GPL-covered software, or incorporates portions of it in code that
  2107. they distribute. This requirement in effect prevents distributors from
  2108. reselling the code as proprietary software. (Distributors may, however, charge
  2109. a fee for distributing the software, and for distributing the source code.)
  2110. The second requirement says that persons who distribute GPL-licensed software
  2111. must make clear that the software's original author provides no warranty
  2112. protection. This requirement protects software authors who are willing to make
  2113. their software widely available but cannot afford to provide extensive
  2114. support.
  2115. Finally, the third requirement says that distributors must place on each copy
  2116. a conspicuous GPL copyright notice, and include an unedited copy of the GPL
  2117. text. Thus, if the software is distributed to yet another distributor, the GPL
  2118. will propagate itself through any succeeding distributions.
  2119. The above description outlines only the major points of the GPL; there are
  2120. numerous details too lengthy to reprint here. Also, the GPL is subject to
  2121. change without notice. For these reasons you may want obtain a copy of the GPL
  2122. text. You can get the GPL text by writing to Free Software Foundation, 675
  2123. Mass Avenue Cambridge, MA 02139.
  2124. Marc Briand
  2125. Managing Editor
  2126. Table 1 Linux FTP sites
  2127. net.tamu.edu
  2128. wcarchive.cdrom.com
  2129. sunsite.unc.edu
  2130. wustl.edu
  2131. tsx-11.mit.edu
  2132. uu.net
  2133. nic.funet.fi
  2134. Table 2 Commercial Sources of Linux
  2135. Softlanding Software (SLS) 910 Lodge Avenue, Victoria British
  2136.  Columbia,
  2137.  Canada; (604)-360-0188. They have several
  2138.  prepackaged distributions which vary from
  2139.  a bare
  2140.  system to a full system with X-Window,
  2141.  TeX,
  2142.  TCP/IP support.
  2143.  
  2144. Linux Systems Labs 49884 Miller Ct., Chesterfield, MI
  2145.  48047-2333; (810)
  2146.  
  2147.  716-1700.
  2148.  
  2149. Walnut Creek (CD-ROM) 4041 Pike Lane, Suite E, Concord, CA 94520;
  2150.  (800)-786-9907.
  2151.  
  2152. America Online (a commercial (800)-827-6364.
  2153. on-line service)
  2154.  
  2155. Just Computers! P.O. Box 751414, Petaluma, CA 94975-1414;
  2156.  (707)-769-1648; linux@justcomp.com.
  2157.  
  2158. InfoMagic Incorporated P.O. Box 30370, Flagstaff, AZ 86003-0370;
  2159. (CD-ROM) (602)-526-9565; info@infomagic.com.
  2160.  
  2161. JANA Publishing (CD-ROM) jana@canrem.com.
  2162.  
  2163. Morse Telecommunications linux@morse.net.
  2164. (CD-ROM)
  2165.  
  2166. Nascent Technology P.O. Box 60669, Sunnyvale, CA 94088-0669;
  2167. (CD-ROM) nascent@netcom.com
  2168.  
  2169. Spheric Microsystems 1900 Empire Blvd. #120, Webster, NY 14580;
  2170. Incorporated (CD-ROM) (800)-869-8649 or (716)-586-4900;
  2171.  info@spheric.com.
  2172.  
  2173. Trans-Ameritech (CD-ROM) roman@trans-ameritech. com
  2174.  
  2175.  
  2176.  
  2177.  
  2178.  
  2179.  
  2180.  
  2181.  
  2182.  
  2183.  
  2184.  
  2185.  
  2186.  
  2187.  
  2188.  
  2189.  
  2190.  
  2191.  
  2192.  
  2193.  
  2194.  
  2195.  
  2196.  
  2197.  
  2198.  
  2199.  
  2200.  
  2201.  
  2202.  
  2203.  
  2204.  
  2205.  
  2206.  
  2207.  
  2208.  
  2209.  
  2210. An Introduction to Floating-Point C Extensions
  2211.  
  2212.  
  2213. Jim Thomas and Jerome T. Coonen
  2214.  
  2215.  
  2216. This article is not available in electronic form.
  2217.  
  2218.  
  2219.  
  2220.  
  2221.  
  2222.  
  2223.  
  2224.  
  2225.  
  2226.  
  2227.  
  2228.  
  2229.  
  2230.  
  2231.  
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237.  
  2238.  
  2239.  
  2240.  
  2241.  
  2242.  
  2243.  
  2244.  
  2245.  
  2246.  
  2247.  
  2248.  
  2249.  
  2250.  
  2251.  
  2252.  
  2253.  
  2254.  
  2255.  
  2256.  
  2257.  
  2258.  
  2259.  
  2260.  
  2261.  
  2262.  
  2263.  
  2264.  
  2265.  
  2266.  
  2267.  
  2268.  
  2269.  
  2270.  
  2271. Advanced C++
  2272.  
  2273.  
  2274. Nimish R. Doshi
  2275.  
  2276.  
  2277. Nimish Doshi is an experienced software engineer currently developing
  2278. graphical user interfaces in New Jersey. He has an M.S. in computer science
  2279. and his computer interests include object-oriented technology and distributed
  2280. applications. He can be reached via e-mail at ej798@cleveland.Freenet.Edu.
  2281.  
  2282.  
  2283.  
  2284.  
  2285. Introduction
  2286.  
  2287.  
  2288. When I first picked up Advanced C++, by Namir Clement Shammas, I was an
  2289. intermediate C++ developer whose first book on the subject was A1 Steven's
  2290. Teach Yourself C++. This first book served as a nice tutorial, but left me
  2291. hungry for more information on how to take advantage of C++'s many rich
  2292. capabilities. That is why I turned to Advanced C++. Snammas's title is
  2293. somewhat of a misnomer, since his book is as much about object-oriented
  2294. programming and data structures as it is about advanced C++. The book is
  2295. divided into three parts, with two-thirds of the material dedicated to data
  2296. structures modeled as C++ classes. This pleasantly provided me much more
  2297. information than I was prepared to grasp!
  2298. The title implies that the book is intended for readers with at least a
  2299. working knowledge of C++. Shammas does not assume his readers are experts
  2300. however. The book is also accessible (an important feature for this kind of
  2301. book, whether you're an expert or not) thanks to Shammas's clear writing style
  2302. and abundant code examples. The examples are annotated with discussions that
  2303. account for alternative ways of implementation. All examples are also included
  2304. on an MS-DOS formatted disk, which accompanies the book. These examples will
  2305. compile with an IBM-PC-compatible C++ compiler (e.g. Borland). The book's
  2306. length (784 pages) may seem intimidating to some, but needlessly so. The extra
  2307. length merely serves the book's purpose to make the C++ programmer proficient.
  2308. I must say that the book fulfills its purpose very well.
  2309.  
  2310.  
  2311. Advanced Components of C++
  2312.  
  2313.  
  2314. Shammas opens up the first twelve chapters with advanced issues of C++. The
  2315. chapters serve as an advanced tutorial rather than a plethora of tricks of the
  2316. language.
  2317. Shammas first reviews reference parameters, default arguments, overloading,
  2318. and template functions. He encourages the use of default parameters to avoid
  2319. excessive overloading of functions, with their multiplicity of signatures. The
  2320. template function is the key to eliminating repetitious code, as it allows the
  2321. same function to handle a variety of data types. Most template functions
  2322. perform some kind of relational comparison. Shammas reminds us to always
  2323. provide relational operators for those classes which are used as actual data
  2324. types for template functions. He provides a simple example of a template
  2325. function which swaps two reference parameters:
  2326. template<class T>
  2327. void swap(T& i, T& j)
  2328. {
  2329. T temp = i;
  2330. i = j;
  2331. j = temp;
  2332. }
  2333. He then goes on to discuss the various roles of member functions, such as
  2334. auxiliary roles and access roles. Another role he describes is that played by
  2335. dormant member functions. A dormant member function is one that can be either
  2336. active or inactive (i.e., do what it's designed to do, or do nothing) for
  2337. different instantiated objects of the same type. C++ provides no built-in
  2338. mechanism to make individual member functions turn themselves on or off. One
  2339. way to add this feature to a class is to include a private integer member in
  2340. the class definition that keeps track of a member function's activation state.
  2341. The class's constructor initializes this integer, which is then updated when
  2342. different actions take place upon the object. Depending on the integer's
  2343. value, functions can either remain dormant or become active. Dormant member
  2344. functions provide a finer granularity in manipulating objects of the same
  2345. class.
  2346. In this first portion of the book Shammas covers a variety of other topics,
  2347. including purposes of static members, a survey of all the C++ operators
  2348. (including a handy example of how the () operator can be used to do almost
  2349. anything), the overloading of new and delete operators, nested declarations,
  2350. template classes, and exception handling.
  2351. Exception handling deserves further mention in this report, since it is one of
  2352. the newest components of the language, modeled after Ada's approach. The
  2353. example in this book illustrates how exception handling can be used to "catch"
  2354. an out-of-bounds condition for an array class. The syntax of exception
  2355. handling is
  2356. try { // statements to try to execute
  2357. }
  2358. catch(exception) { // statements
  2359. attempting to deal with
  2360. // exception }
  2361. When any statement inside the try block reaches a predefined error condition,
  2362. that statement will raise an exception by using the keyword throw followed by
  2363. the exception type. The catch statement will respond to the error condition.
  2364. In Shammas's examples, the catch statement simply prints a message to cout and
  2365. exits.
  2366.  
  2367.  
  2368. Object Oriented Programming
  2369.  
  2370.  
  2371. The next part of the book provides numerous examples of inheritance, ranging
  2372. from shapes to n-dimensional objects such as matrices. The best section here
  2373. is the discussion on object behavior. Shammas keeps things interesting by
  2374. demonstrating real-world behavior of objects, with examples from several
  2375. realms.
  2376. Many people see objects as mere state holders, with the private members
  2377. representing the state of the object. However, an object is semantically more
  2378. than just a bunch of combinatorial Boolean values. For instance, Shammas ably
  2379. demonstrates this with an example of an object representing a plane and its
  2380. engines. Depending on what messages are passed to the object and its internal
  2381. state (e.g. fuel supply), the object's "flying behavior" will change. When all
  2382. four of its engines fail, the object invokes a private method called shutdown.
  2383. This action effectively disables the object, modeling semantically what would
  2384. happen in the real world. (Of course, in the real world, the plane would
  2385. probably make an emergency landing.)
  2386. Another, more scientific example is that of objects representing chemical
  2387. elements and molecules. When two elements, such as hydrogen and oxygen,
  2388. chemically react, a portion of one element usually remains which was not
  2389. involved in the reaction. Modeling this reaction with Boolean values will not
  2390. work. One way that will work is to give these objects weights. When a function
  2391. is used to simulate the chemical reactions, the weights change as one object
  2392. is completely used up while the residue of another remains. This is Shammas's
  2393. concept of a transformed object. The capacity of an object to undergo a
  2394. metamorphosis is what gives it real-world behavior. Shammas elegantly
  2395. illustrates this point in the book.
  2396.  
  2397.  
  2398. Class Templates
  2399.  
  2400.  
  2401. Shammas's brief treatment of object behavior is a sidetrack to the main part
  2402. of his book. The main part deals with data structures -- represented by C++
  2403. classes holding template data. As I have not dealt with some of these
  2404. structures since my sophomore year in college, I found this an interesting
  2405. review. The data structures covered in this book are
  2406. An extensive string class
  2407. A token class which builds upon the string class
  2408. An array class equipped with multiple sorting and searching member functions
  2409. A list class
  2410.  
  2411. Binary tree, AVL tree, red/black tree, and array tree classes
  2412. A character set class
  2413. Internal and external hash table classes
  2414. M-Way tree and B-tree classes
  2415. Each chapter introduces the properties of the data structure and then provides
  2416. a possible implementation. All of the container data structures, such as
  2417. lists, are modeled as template classes. One of the most exotic data structures
  2418. presented is an internal hash table which uses AVL trees as entries to speed
  2419. up searches during collisions. (An AVL tree is a height-balanced binary tree
  2420. of sorted data entries.)
  2421. Advanced C++ serves as a perfect model for anyone who is interested in
  2422. creating a template class library for reusable data structures. A lot of
  2423. people simply don't use many of the data structures they spent so much time
  2424. studying in school. That's because many of the structures are tedious to
  2425. implement and must be reimplemented for various data types. The real boon of
  2426. template classes is that once they are created, they prove to be quite
  2427. reusable with little or no modification. Generality is the programmer's dream
  2428. come true for avoiding repetitious coding. In Shammas's examples, most of the
  2429. class members are protected, implying that inheritance is the way to enhance
  2430. the behavior of the class. I think Shammas does an excellent job in presenting
  2431. his template classes and associated member functions. The only thing I don't
  2432. like is that the examples utilizing the template data structures are too
  2433. mundane and simple. I think it would have been more interesting if Shammas had
  2434. combined real-world object behavior with the template data structures.
  2435.  
  2436.  
  2437. Still Hungry
  2438.  
  2439.  
  2440. As I said before, when I first picked up, Advanced C++, I did not expect such
  2441. an extensive treatment of data structures. I almost think the title of the
  2442. book should have been Advanced C++ and C++ Data Structures. If Shammas
  2443. overemphasizes one topic, it seems to be at the expense of a few others -- for
  2444. example, polymorphism, inheritance, and memory management.
  2445. Some would say that polymorphism -- the ability of pointers, references, and
  2446. member functions to take on new behaviors in derived data types at run time --
  2447. is the foundation of object-oriented programming. Certainly, it is an
  2448. important topic as it relates to C++. Yet Shammas does not deal with
  2449. polymorphism in any great detail. This is in contrast to some books which
  2450. devote much of their space to lengthy discussions of virtual functions. It may
  2451. be that with the addition of templates, the need for polymorphism in C++ to
  2452. simulate genericity may diminish. Nonetheless, I believe any discussion of
  2453. advanced C++ issues should cover this topic extensively.
  2454. Another omission -- of which almost all C++ books are guilty -- is a
  2455. discussion on when to use multiple inheritance. This book is no exception, as
  2456. it presents few good examples of this concept. Of course, some Smalltalk
  2457. purists will say that it is never good practice to engage in multiple
  2458. inheritance. However, there are times when multiple inheritance may actually
  2459. model real-world behavior very well. For instance, in Bertrand Meyer's famous
  2460. book, Object Oriented Software Construction, he mentions a class of drivers
  2461. which are American and French. It makes sense for this class of drivers to
  2462. inherit from both the American and French classes. Perhaps multiple
  2463. inheritance leads to more complex implementations, but that does not mean
  2464. programmers should avoid it in all cases. We need some good examples.
  2465. Finally, in today's programming environment, object-oriented programming often
  2466. leads to memory leaks, as this type of development usually necessitates the
  2467. need to allocate memory for dynamic objects. Sometimes developers become
  2468. careless and inadvertently forget to free up the space for the object once it
  2469. is no longer needed. Although Advanced C++ is not a book about the pitfalls of
  2470. C++, I would have liked to have seen more mention of this critical issue.
  2471.  
  2472.  
  2473. A Good Teacher
  2474.  
  2475.  
  2476. Shammas is a good teacher. Like all good teachers, he has organized his course
  2477. well, first presenting advanced language components and then laying out class
  2478. templates for data structures. (In keeping with this approach, he presents the
  2479. most difficult data structure, the B-tree, last.)
  2480. This is not a cookbook. Programmers looking for the proverbial 1,001 C++
  2481. tricks will not find them here. Rather, I recommend this book for the
  2482. intermediate C++ user because it is a very readable tutorial and can serve as
  2483. reference for various topics. Most of the examples in the book are generic
  2484. enough to be slightly modified to work under any modern C++ compiler. Shammas,
  2485. an accomplished author, has provided the C++ user community a service by
  2486. writing this book.
  2487. Title: Advanced C++
  2488. Author: Namir Clement Shammas
  2489. Publisher: SAMS Publishing
  2490. Pages: 784
  2491. Price: $39.95
  2492. ISBN: 0-672-30158-X
  2493.  
  2494.  
  2495.  
  2496.  
  2497.  
  2498.  
  2499.  
  2500.  
  2501.  
  2502.  
  2503.  
  2504.  
  2505.  
  2506.  
  2507.  
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.  
  2516.  
  2517.  
  2518.  
  2519.  
  2520.  
  2521.  
  2522.  
  2523.  
  2524.  
  2525.  
  2526.  
  2527.  
  2528. The Best C/C++ Tips Ever
  2529.  
  2530.  
  2531. P.J. Plauger
  2532.  
  2533.  
  2534. P.J. Plauger is senior editor of C/C++ Users Journal. He is convener of the
  2535. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  2536. latest books are The Draft Standard C++ Library, and Programming on Purpose
  2537. (three volumes), all published by Prentice-Hall. You can reach him at
  2538. pjp@plauger.com.
  2539.  
  2540.  
  2541. This is a dangerous book. It purports to show 405 tips that "lead you past the
  2542. traps and pitfalls of the language and show you how you might improve your
  2543. programs," according to the Introduction. (I give no page numbers in this
  2544. review because the book has nary a numbered page.) As such, it is obviously
  2545. modeled on Effective C++, the excellent book by Scott Meyers (Addison-Wesley,
  2546. 1992) that is organized around 50 "items." But where Meyers is always on the
  2547. money, Porter's book short changes you most of the time. Even worse, it is
  2548. also laced with counterfeit advice.
  2549. Some of the tips are just plain wrong. Tip 23 tells us, "There is No Need to
  2550. Use the enum Keyword When Declaring Instances of an enum." True enough for
  2551. C++, perhaps, but it's in a section ostensibly aimed at C programmers. Then
  2552. Tip 25 says, "The Equivalence Between Arrays and Pointers to Arrays Is Only
  2553. Valid for Function Arguments." Even if you gloss over the erroneous statement
  2554. of equivalence, the stated restriction is still untrue.
  2555. Other tips are so badly stated as to be vacuous. Tip 68: "In Nested if
  2556. Statements, a Dangling else Statement Goes with the Preceding if Statements."
  2557. Where else? Or they are a matter of considerable debate, as with Tip 69: "A
  2558. switch Statement Is More Efficient Than Nested if-else Statements." Or Tip 77:
  2559. "It Is Faster to Use an Element Pointer Rather Than a Subscript when Scanning
  2560. Arrays." As a general rule, if Porter makes a statement about efficiency, you
  2561. can be reasonably certain his view is microscopic and his experience extremely
  2562. limited. In fact, I soon learned to be wary of any generalization he makes
  2563. about "most" computers or compilers.
  2564. When he is not outright wrong, Porter confuses with fuzzy writing. The first
  2565. page of tips, for example, uses the following terms for objects with dynamic
  2566. lifetimes: functions variables [sic], function-local variables, items on the
  2567. stack, an item declared in a function, and local items. Given the level at
  2568. which the advice is nominally aimed, such colloquial inexactitude is
  2569. intolerable. (I personally find it unacceptable even in advanced writings on C
  2570. and C++.)
  2571. He purports to give advice about standards conformance, but it is clear the
  2572. author is vague about what's in the C Standard. Chapter 11 is called "The
  2573. Standard C Library," and the Introduction says it covers "the ANSI C
  2574. functions." But Tip 299 is about "Using swab" to Reverse Byte Order." That
  2575. function is not in the Standard C library. Nor is the function putenv,
  2576. discussed in Tip 301. Tip 302 has an archaic description of the character
  2577. classification functions. Tip 303 misspells the tmpnam function. Tip 304 has a
  2578. parochial description of the POSIX (not Standard C) function fstat. Tip 305
  2579. gives erroneous information about determining file lengths. And so on, and so
  2580. on.
  2581. It is tempting to say that this book contains an error on every page, but I
  2582. can't say that for sure. For one thing, C++ is sufficiently complex and
  2583. unstandardized to date to offer lots of wiggle room. It could be that you
  2584. could go for pages on end in that area and get advice that's as good as
  2585. anybody else's. But I wouldn't bet the farm on it.
  2586. The author has obviously accreted considerable lore over years of writing C
  2587. code, primarily under MS-DOS it seems. It frightens me, however, that he can
  2588. be so far off base in so many critical areas and still consider himself an
  2589. expert. It saddens me that major publishers still let stuff like this get into
  2590. print and occupy precious shelf space in book stores. It depresses me that
  2591. thousands of innocents will still buy this book and be befuddled by such
  2592. meretricious advice.
  2593. Normally, I try to find some redeeming social value in any book I review. Snob
  2594. that I am, I have to concede that a given book usually has something of value
  2595. to somebody, even if it falls short of my lofty standards. In this case,
  2596. however, I can't. Rather, I wish for once that Tipper Gore could push through
  2597. mandatory warning labels for books on C and C++ that actively mislead. Kids,
  2598. what you are seeing is performed by an untrained professional. Don't try this
  2599. at home.
  2600. Title: The Best C/C++ Tips Ever
  2601. Author: Anthony Porter
  2602. Publisher: McGraw-Hill
  2603. Pages: approximately 500
  2604. Price: $29.95
  2605. ISBN: 0-07-881820-6
  2606.  
  2607.  
  2608.  
  2609.  
  2610.  
  2611.  
  2612.  
  2613.  
  2614.  
  2615.  
  2616.  
  2617.  
  2618.  
  2619.  
  2620.  
  2621.  
  2622.  
  2623.  
  2624.  
  2625.  
  2626.  
  2627.  
  2628.  
  2629.  
  2630.  
  2631.  
  2632.  
  2633.  
  2634.  
  2635.  
  2636.  
  2637.  
  2638.  
  2639.  
  2640.  
  2641.  
  2642.  
  2643.  
  2644. Standard C/C++
  2645.  
  2646.  
  2647. The Header <strstream>
  2648.  
  2649.  
  2650.  
  2651.  
  2652. P.J. Plauger
  2653.  
  2654.  
  2655. P.J. Plauger is senior editor of C/C++ Users Journal, He is convener of the
  2656. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  2657. latest books are The Draft Standard C++ Library, and Programming on Purpose
  2658. (three volumes), all published by Prentice-Hall. You can reach him at
  2659. pjp@plauger.com.
  2660.  
  2661.  
  2662.  
  2663.  
  2664. Special Notice -- C++ Draft Public Review
  2665.  
  2666.  
  2667. The US public review of the draft C++ standard is scheduled to begin as early
  2668. as April 1995. For detailed information on how to get a copy of the draft and
  2669. how to submit comments, send a message to the reflector
  2670. c++std-notify@research.att.com. -- pjp
  2671.  
  2672.  
  2673. Introduction
  2674.  
  2675.  
  2676. The header <strstream> defines three classes that cooperate to help you read
  2677. and write character sequences stored in memory (a.k.a. "string buffers"):
  2678. strstreambuf, derived from streambuf to mediate access to an in-memory
  2679. character sequence and grow it on demand
  2680. istrstream, derived from istream to construct a strstreambuf object with an
  2681. input stream and to assist in extracting from the stream
  2682. ostrstream, derived from ostream to construct a strstreambuf object with both
  2683. input and output streams and to assist in inserting into the stream
  2684. An in-memory character sequence is, in many ways, the simplest kind of stream
  2685. buffer. The buffer maintained by the controlling streambuf object is not
  2686. merely a window on some separate representation, like an external file.
  2687. Rather, the buffer is the character sequence in its entirety.
  2688. Nevertheless, a strstreambuf object can control a variety of in-memory
  2689. character sequences:
  2690. an array of characters all of whose elements are defined from the outset
  2691. an array of characters whose initial elements contain a null-terminated string
  2692. an initially empty array of characters that can grow on demand, with storage
  2693. allocated by new expressions and freed by delete expressions
  2694. an initially empty array of characters that can grow on demand, with storage
  2695. allocated and freed by functions supplied by the program
  2696. All these choices are reflected in a plethora of constructors for the three
  2697. classes defined in <strstream>.
  2698.  
  2699.  
  2700. The Simple Choices
  2701.  
  2702.  
  2703. Despite all the options, three simple choices stand out. You can initialize an
  2704. istrstream object to control a constant array of characters. You can then
  2705. extract from its associated input stream, but you cannot insert to the output
  2706. stream. The result is effectively an istream object where you dictate the
  2707. stream contents purely within the program. Or you can initialize an ostrstream
  2708. object to control a non-constant array of characters. You can then insert into
  2709. its associated output stream to store into the array.
  2710. Your third simple choice is to initialize an ostrstream object to control an
  2711. initially empty dynamic output stream that can grow on demand. The result is
  2712. effectively an ostream object where you capture the stream contents purely
  2713. within the program. You can then capture the final stream contents before the
  2714. ostrstream object is destroyed. Class strstreambuf supplies several member
  2715. functions to help you capture these contents. For an object x of class
  2716. strstreambuf, here's what you can do:
  2717. Call x.str() to obtain a pointer to the start of the controlled character
  2718. sequence. The function freezes the racter sequence to prevent further
  2719. insertions. Freezing also prevents the destructor for x from freeing the
  2720. character sequence -- you assume that responsibility when you obtain the
  2721. pointer.
  2722. Call x.freeze() to freeze the character sequence, or x. freeze(0) to unfreeze
  2723. it. The latter call is particularly helpful if you've learned what you need
  2724. from an earlier x.str() call and now want the destractor to free the character
  2725. sequence for you.
  2726. Call x. pcount() to count the number of characters inserted in the sequence.
  2727. The count is useful information for arbitrary character values, or if you
  2728. don't insert a null character at the end of the character sequence.
  2729. Here is the obvious use for the manipulator ends, by the way. (See "Standard
  2730. C/C++: The Header <ostream>," CUJ, September 1994.) It's a clear way to supply
  2731. a null character at the end of an in-memory character sequence.
  2732. I end this brief introduction by mentioning the ghost at the banquet table.
  2733. The Standard C library has long supported a similar capability. You can obtain
  2734. formatted input from an in-memory character sequence by calling sscanf. You
  2735. can write formatted output to an in-memory character sequence by calling
  2736. sprintf or vsprintf. (All three functions are declared in <stdio.h>.) So how
  2737. are these three classes any better? The answer is easy:
  2738. An istrstream object looks like any other istream object controlling an input
  2739. stream. And an ostrstream object looks like any other ostream object
  2740. controlling an output stream. The older functions oblige you to know that
  2741. you're dealing with an in memory character sequence. And you always have to
  2742. know exactly where the sequence resides.
  2743. Unlike sscanf, an istrstream object can control a sequence of arbitrary
  2744. character values. It can have embedded null characters. Equally, it doesn't
  2745. need a terminating null character.
  2746. sprintf writes to an array of fixed length whose length is nevertheless not
  2747. known to the function. You avoid storage overwrites only by restricting every
  2748. single conversion specification. By contrast, an ostrstream object can control
  2749. a sequence of known length. Insertions fail before storage overwrite occurs.
  2750. Or the object can control a dynamic sequence of arbitrary length. Insertions
  2751. fail only when heap storage is exhausted.
  2752. I believe these are ample reasons to cultivate a knowledge of the header
  2753. <strstream>.
  2754.  
  2755.  
  2756. What the Draft Standard Says
  2757.  
  2758.  
  2759.  
  2760. Class strstreambuf is the first of several classes derived from streambuf. (I
  2761. describe more in future installments, on the headers <sstream> and <fstream>.)
  2762. What makes each such stream buffer unique is the way it overrides the virtual
  2763. member functions in the base class. The exception classes exhibit uniqueness
  2764. in a small way, by overriding the virtual member function do_raise. (See
  2765. "Standard C: The Header <exception>," CUJ, February 1994.) But the stream
  2766. buffers indulge in specialization big time.
  2767. The convention within the draft C++ Standard is to comment out virtual member
  2768. functions in the derived class. Each such comment is labeled inherited. The
  2769. suggestion is that an implementation need not provide an overriding definition
  2770. if the definition in the base class does the job.
  2771. For the classes derived from streambuf, however, these comments are often
  2772. misleading. Listing 1, for example, shows how the draft C++ Standard defines
  2773. the class strstreambuf. Several critical virtual member functions must have
  2774. overriding definitions to satisfy the semantic requirements of the class. (The
  2775. descriptions of these functions in the draft C++ Standard are often hard to
  2776. read as well. They represent standardese at its legalistic extreme. But those
  2777. descriptions are also the definitive word on how a class behaves in peculiar
  2778. circumstances. Turn to them when tutorials prove inadequate.)
  2779. Listing 2 shows how the draft C++ Standard defines the class istrstream, and
  2780. Listing 3 shows how it defines the class ostrstream. These are derived from
  2781. the classes istream and ostream, respectively, to facilitate operations with
  2782. stream buffers of class strstreambuf.
  2783.  
  2784.  
  2785. Using the Header
  2786.  
  2787.  
  2788. You include the header <strstream) to make use of any of the classes
  2789. istrstream, ostrstream, or strstreambuf. Objects of these classes let you read
  2790. and write in-memory character sequences just as if they were conventional
  2791. files. You can choose among four patterns of access:
  2792. read only
  2793. write only
  2794. simple read/write
  2795. sophisticated read/write
  2796. I deal with each of these options in turn.
  2797.  
  2798.  
  2799. Read-Only String Buffers
  2800.  
  2801.  
  2802. If all you want to do is read an in-memory character sequence, construct an
  2803. object of class istrstream to specify the character sequence and control
  2804. extractions from it. For a null-terminated string s, you can write:
  2805. istrstream strin(s);
  2806. The character sequence begins at s and continues up to, but not including, the
  2807. terminating null character that follows.
  2808. To control an array of n characters s with arbitrary values, you can write:
  2809. istrstream strin(s, n);
  2810. In either case, s can be either a pointer to char or a pointer to const char.
  2811. Whichever way you construct the istrstream object, the resultant stream buffer
  2812. (pointed at by strin.rdbuf()) does not support insertions. You must specify an
  2813. initial character sequence -- class istrstream has no default constructor. The
  2814. character sequence remains unchanged for the life of the istrstream object. (I
  2815. suggest you refrain from altering the contents of the character sequence by
  2816. other means, if that is possible, while the istrstream object controls
  2817. accesses to it.)
  2818. Positioning within a read-only stream is fairly simple. A streamoff value is
  2819. effectively an index into the character sequence. Thus:
  2820. strin. rdbuf()->pubseekoff(0 ios::beg);
  2821. sets the stream position at the beginning (position zero) of the character
  2822. sequence. If the length of the sequence is nonzero, the next character
  2823. extracted will be the first character in the sequence. Any attempt to set the
  2824. stream position to a negative value, or beyond the end of the character
  2825. sequence, will fail. You can also call streambuf::pubseekpos, as above, but
  2826. that is less necessary for an in-memory character sequence. Do so for code
  2827. that shouldn't need to know what flavor stream buffer it is really dealing
  2828. with. (For more discussion of stream positioning, see "Standard C: The Header
  2829. <streambuf>," CUJ, June 1994.)
  2830. For what it's worth, you can also call strin.str() for a read-only character
  2831. sequence. The call does not freeze the character sequence, since it is not
  2832. dynamically alterable. The function merely returns a pointer to the beginning
  2833. of the character sequence, just as for a character sequence you construct (as
  2834. described below). This is always the beginning of the character sequence you
  2835. specified when constructing the object.
  2836. If you do call strin.str() for a read-only character sequence as above, be
  2837. warned. The call strin.rdbuf()->pcount() will not return the length of the
  2838. sequence. Since no output stream exists, this call returns zero. You must
  2839. determine the length of the sequence by some other means, such as positioning
  2840. the stream at the end and inspecting the resultant stream offset.
  2841.  
  2842.  
  2843. Write-Only String Buffers
  2844.  
  2845.  
  2846. If all you want to do is create an in-memory character sequence, construct an
  2847. object of class ostrstream to control insertions into it. You can write:
  2848. ostrstream strout;
  2849. then insert into strout just like any other output stream. The character
  2850. sequence can grow dynamically to arbitrary length. (The actual limit is
  2851. usually INT_MAX, defined in <limits.h>, or when a storage allocation request
  2852. fails.)
  2853. If you want a null-terminated string, be sure to insert a null character last,
  2854. as with:
  2855. strout << ends;
  2856. It will not be supplied for you when you call strout.str(). For a sequence of
  2857. arbitrary characters, you can determine the number of characters you inserted
  2858. by calling strout.pcount(). But see the warning below.)
  2859. Positioning within a write-only stream is also fairly simple. You can work
  2860. mostly with streamoff values, as for read-only streams described above. A few
  2861. caveats are in order, however:
  2862. Current practice seems to vary on where you can set the stream position. The
  2863. safest bet is to move only to character positions with values already defined
  2864. -- either at construction or as a result of earlier insertions.
  2865. Some confusion is also endemic about the interaction of stream positioning
  2866. requests and the value returned by a call such as strout.pcount().
  2867. Even an implementation that obeys the draft C++ Standard can still surprise.
  2868. Strictly speaking, ostrstream::pcount calls strstreambuf::pcount, which does
  2869. not return a count of all insertions. Rather, it returns the difference
  2870. between the current output stream position and the initial output stream
  2871. position. The former can be left other than at the end of the character
  2872. sequence by stream-positioning requests. The latter can be set beyond the
  2873. beginning of the character sequence by a more sophisticated strstreambuf
  2874. constructor, as described below.
  2875. As usual, my advice is to avoid such delicate areas in code you hope to keep
  2876. portable. If you insist on pushing the limits of clearly defined behavior,
  2877. expect surprises.
  2878. Your goal in creating a write-only character sequence is to capture the final
  2879. result, as a rule. Call strout.str() to freeze the character sequence and
  2880. return a pointer to its initial character. Remember that freezing the
  2881. character sequence tells the strstreambuf destructor not to free storage for
  2882. the character sequence. You must either free the storage yourself or be sure
  2883. to call strout.freeze(0) before strout is destroyed.
  2884.  
  2885.  
  2886. Simple Read/Write String Buffers
  2887.  
  2888.  
  2889. If you want to create an in-memory character sequence that you can read as
  2890. well as write, you need two objects to control the input and output streams.
  2891. First, construct an object of class ostrstream to supply the strstreambuf
  2892. object and control insertions, then construct an object of class istream to
  2893. control extractions. You can write:
  2894. ostrstream strout;
  2895. istream strin(strout. rdbuf());
  2896. much as I have described several times in earlier installments for setting up
  2897. a bidirectional stream. (See, for example, "Standard C: Introduction to
  2898. Iostreams," CUJ, April 1994.) You can then insert into strout just like any
  2899. other output stream. And once you have inserted characters, you can extract
  2900. them from strin just like any other input stream.
  2901. A few caveats are in order, however. Try to extract beyond the last character
  2902. in the sequence and you will encounter end-of-file. Once you extend the
  2903. sequence by inserting characters, you can successfully extract again, but not
  2904. until you clear eofbit if it is set in the istream object. Once again, this
  2905. status bit proves less than trustworthy.
  2906. You can position the input and output streams separately or jointly. As usual,
  2907. positioning operations demand a modicum of caution:
  2908.  
  2909. The draft C++ Standard specifies a default third argument value which =
  2910. ios::in ios::out for streambuf::pubseekoff. (It specifies the same for the
  2911. default second argument to streambuf::pubseekpos.) Thus, unless you supply an
  2912. actual which argument that selects only one stream -- ios::in or ios::out -- a
  2913. call to this member function endeavors to position both streams in tandem.
  2914. Seldom does that make sense. When both reading and writing an in-memory
  2915. character sequence, always specify the which argument on such calls.
  2916. The draft C++ Standard is even nastier at times. If the second argument to
  2917. streambuf::pubseekof is ios::cur, a tandem positioning operation will fail.
  2918. For such relative positioning requests, you should always specify a which
  2919. argument that selects only one stream.
  2920. Note that the second caveat applies even to read-only or write-only in-memory
  2921. character sequences.
  2922. As an important aside, here is a warning regarding the member function rdbuf.
  2923. Several classes in the Standard C++ library are derived from either istream or
  2924. ostream. These include istrstream and ostrstream, in this particular header.
  2925. All such derived classes also provide a member function rdbuf() that hides the
  2926. member function in the base class ios.
  2927. Why is this so? All such derived classes also provide a member object of a
  2928. class derived from streambuf. In this header, that derived class happens to be
  2929. strstreambuf. Consider, for example, the call istr.rdbuf(), for an object istr
  2930. of class istrstream. It returns a pointer to the strstreambuf member object in
  2931. istr. And the return type is indeed pointer to strstreambuf, not pointer to
  2932. streambuf as in the base class ios.
  2933. A generic pointer to the base streambuf gets you to all the inherited member
  2934. functions. It even gets you to the proper overriding definitions of virtual
  2935. member functions. But to access any member functions peculiar to the derived
  2936. class, you need such a specialized pointer. Again in this particular case, the
  2937. member functions freeze, pcount, and str are peculiar to class strstreambuf,
  2938. not its base class.
  2939. Potential confusion arises, however, once you make a call such as
  2940. istr.rdbuf(strout.rdbuf()). The critical pointer in the ios subobject now
  2941. designates a different stream buffer. But a subsequent call to istr.rdbuf()
  2942. still returns a pointer to the member object within istr. To get the pointer
  2943. actually used by inserters and extractors, you must make the hairy call
  2944. ((ios&)istr).rdbuf().
  2945. Got all that? If not, don't worry too much about it. The bottom line is that
  2946. you should follow a fundamental style rule. Alter the stored stream buffer
  2947. pointer only in an object of class istream or ostream. Never do so in an
  2948. object of a class derived from one of these. Even better, do as I did above --
  2949. use a special istream or ostream object constructed from the outset to share
  2950. an existing stream buffer. With either approach, there's never a second stream
  2951. buffer lying around to cause confusion.
  2952.  
  2953.  
  2954. Sophisticated String Buffers
  2955.  
  2956.  
  2957. You can get even fancier with the classes defined in <strstream>. What follows
  2958. is an assortment of more sophisticated setups.
  2959. Say you want to use an existing character array to store a character sequence,
  2960. and you want to read the character sequence once you write it. If you declare:
  2961. ostrstream strout(s, n);
  2962. istream strin(strout.rdbuf());
  2963. then strout controls the sequence of n characters beginning at s. Both the
  2964. read position and the write position are at the beginning of the character
  2965. sequence. In this case, you cannot extend the sequence by inserting at or
  2966. beyond character position n.
  2967. Here's a variation on this scenario. Say you want to use an existing character
  2968. array to store a character sequence, and the array already stores a
  2969. null-terminated string. You want to begin extracting from the beginning of the
  2970. character sequence, but you want to begin inserting at the end of the
  2971. null-terminated string (first replacing the terminating null). If you declare:
  2972. ostrstream strout(s, n, ios::app);
  2973. istream strin(strout.rdbuf());
  2974. then you get the desired behavior. Insertions effectively append characters to
  2975. an initial null-terminated string, but won't continue past the end of the
  2976. character array. For extractions, see the caveat above about the behavior of
  2977. eofbit.
  2978. If you want to get fancier, you have to construct a strstreambuf object
  2979. directly. This class has lots of constructors with lots of options. I describe
  2980. here just the two that I feel are genuinely useful and safe.
  2981. Say you want to construct an in-memory character sequence that you know will
  2982. get very large. You'd like to suggest, when constructing the strstreambuf
  2983. object, that the object allocate space for a large sequence right off the
  2984. mark. That can save lots of execution overhead in reallocating storage as the
  2985. character sequence grows. Equally, you might want to construct a number of
  2986. character sequences all of which will be fairly small. You'd like to suggest
  2987. that each such object allocate only a small number of characters. That can
  2988. save lots of unused storage. If you declare:
  2989. strstreambuf sb(n);
  2990. istream strin(&sb);
  2991. ostream strout(&sb);
  2992. for some int value n, the constructor should take the hint. What it does with
  2993. it is up to the implementation, but at least you get to make the suggestion.
  2994. Another approach to storage allocation is to do the job yourself. So your
  2995. final interesting choice is to begin with two functions like:
  2996. #include <stdlib. h>
  2997.  
  2998. void *my_alloc(size_t n)
  2999. { // allocate n chars
  3000. return (malloc(n));
  3001. }
  3002.  
  3003. void my_free(void *p)
  3004. { // free allocated storage
  3005. free(p);
  3006. }
  3007. Tailor these basic functions as you see fit. Then you can declare:
  3008. strstreambuf sb(&my_alloc,
  3009. &my_free);
  3010. istream strin(&sb);
  3011. ostream strout(&sb);
  3012. The two functions you supply will be called, in place of new and delete
  3013. expressions, to allocate and free storage for character sequences.
  3014.  
  3015.  
  3016. Next Month
  3017.  
  3018.  
  3019. That's a capsule summary of how to use string buffers, as specified by the
  3020. draft C++ Standard. Next month, I'll show you one way to implement this
  3021. header, and discuss some of the interesting challenges it presents.
  3022. This article is excerpted in part from P.J. Plauger, The Draft Standard C++
  3023. Library, (Englewood Cliffs, N.J.: Prentice-Hall, 1995.)
  3024.  
  3025. Listing 1 The class strstreambuf
  3026. class strstreambuf : public streambuf {
  3027. public:
  3028. strstreambuf(int alsize_arg: 0);
  3029. strstreambuf(void* (*palloc_arg)(size_t),
  3030.  
  3031. void (*pfree_arg)(void*));
  3032. strstreambuf(char* gnext_arg. int n. char* pbeg_arg = 0);
  3033. strstreambuf(unsigned char* gnext arg. int n,
  3034. unsigned char* pbeg_arg = 0);
  3035. strstreambuf(signed char* gnext_arg, int n,
  3036. signed char* pbeg_arg = 0);
  3037. strstreambuf(const char* gnext_arg, int n);
  3038. strstreambuf(const unsigned char* gnext_arg, int n);
  3039. strstreambuf(const signed char* gnext_arg. int n);
  3040. virtual ~strstreambuf();
  3041. void freeze(int = 1);
  3042. char* str();
  3043. int pcount();
  3044. protected:
  3045. // virtual int overflow(int c = EOF); inherited
  3046. // virtual int pbackfail(int c = EOF); inherited
  3047. // virtual int underflow(); inherited
  3048. // virtual int uflow(); inherited
  3049. // virtual int xsgetn(char* s. int n); inherited
  3050. // virtual int xsputn(const char* s. int n); inherited
  3051. // virtual streampos seekoff(streamoff off, ios::seekdir way,
  3052. // ios::openmode which = ios::in ios::out); inherited
  3053. // virtual streampos seekpos(streampos sp,
  3054. // ios::openmode which = ios::in ios::out); inherited
  3055. // virtual streambuf* setbuf(char* s. int n); inherited
  3056. // virtual int sync(); inherited
  3057. private:
  3058. // typedef T1 strstate; exposition only
  3059. // static const strstate allocated; exposition only
  3060. // static const strstate constant; exposition only
  3061. // static const strstate dynamic; exposition only
  3062. // static const strstate frozen; exposition only
  3063. // strstate strmode; exposition only
  3064. // int alsize; exposition only
  3065. // void* (*palloc)(size_t); exposition only
  3066. // void (*pfree)(void*); exposition only
  3067. };
  3068.  
  3069.  
  3070. Listing 2 The class istrstream
  3071. class istrstream: public istream {
  3072. public:
  3073. istrstream(const char* s);
  3074. istrstream(const char* s, int n);
  3075. istrstream(char* s);
  3076. istrstream(char* s, int n);
  3077. virtual ~istrstream();
  3078. strstreambu* rdbuf() const;
  3079. char *str();
  3080. private;
  3081. // strstreambuf sb; exposition only
  3082. };
  3083.  
  3084.  
  3085. Listing 3 The class ostrstream
  3086. class ostrstream: public ostream {
  3087. public:
  3088. ostrstream();;
  3089. ostrstream(char* s, int n, openmode mode = out);
  3090.  
  3091. virtual ~ostrstream();
  3092. strstreambuf* rdbuf() const;
  3093. void freeze(int freezel = 1);
  3094. char *str();
  3095. int pcount() const;
  3096. private;
  3097. // strstreambuf sb; exposition only
  3098. };
  3099.  
  3100.  
  3101.  
  3102.  
  3103.  
  3104.  
  3105.  
  3106.  
  3107.  
  3108.  
  3109.  
  3110.  
  3111.  
  3112.  
  3113.  
  3114.  
  3115.  
  3116.  
  3117.  
  3118.  
  3119.  
  3120.  
  3121.  
  3122.  
  3123.  
  3124.  
  3125.  
  3126.  
  3127.  
  3128.  
  3129.  
  3130.  
  3131.  
  3132.  
  3133.  
  3134.  
  3135.  
  3136.  
  3137.  
  3138.  
  3139.  
  3140.  
  3141.  
  3142.  
  3143.  
  3144.  
  3145.  
  3146.  
  3147.  
  3148.  
  3149.  
  3150.  
  3151.  
  3152.  
  3153.  
  3154. Code Capsules
  3155.  
  3156.  
  3157. The Standard C Library, Part 1
  3158.  
  3159.  
  3160.  
  3161.  
  3162. Chuck Allison
  3163.  
  3164.  
  3165. Chuck Allison is a regular columnist with CUJ and a Senior Software Engineer
  3166. in the Information and Communication Systems Department of the Church of Jesus
  3167. Christ of Latter Day Saints in Salt Lake City. He has a B.S. and M.S. in
  3168. mathematics, has been programming since 1975, and has been teaching and
  3169. developing in C since 1984. His current interest is object-oriented technology
  3170. and education. He is a member of X3J16, the ANSI C++ Standards Committee.
  3171. Chuck can be reached on the Internet at 72640.1507@compuserve.com.
  3172.  
  3173.  
  3174. Although it may not seem like it, C is a very small language. In fact, it was
  3175. first implemented on a very small platform by today's standards. The first C
  3176. compiler I owned ran on a Commodore 64! C's simplicity and compactness make it
  3177. ideal for systems programming and for developing programs that run in embedded
  3178. systems, such as in automobiles or cameras.
  3179. A key difference between C in such freestanding environments and C in a hosted
  3180. environment, such as a desktop or mid-range computer, is the presence of the
  3181. Standard C library. In a freestanding environment a conforming compiler only
  3182. needs to provide the types and macros specified in <float.h>, <limits.h>,
  3183. <stdarg.h>, and <stddef.h>. By contrast, in hosted environments, programmers
  3184. who work on typical data processing projects take the library for granted --
  3185. in fact, they think of it as part of the language. A large portion of everyday
  3186. C code consists of library calls. Even I/O facilities like printf and scanf
  3187. are part of the library, not the language.
  3188. The Standard C library consists of functions, type definitions, and macros
  3189. declared in fifteen header files. Each header more or less represents a domain
  3190. of programming functionality, such as I/O or string processing operations.
  3191. Some macros and type definitions, such as NULL and size_t, appear in more than
  3192. one header file for convenience.
  3193. In this article I divide the Standard library into three groups (see Table 1
  3194. through Table 3). Group I represents library components that you should
  3195. understand thoroughly if you want to consider yourself a C programmer. Too
  3196. often I have seen programs that "reinvent" basic library facilities such as
  3197. memcpy or strchr. Such programs should not be executed, but sometimes I think
  3198. their programmers should be (or at least fired). To receive your paycheck in
  3199. good conscience, though, you should really master Group II as well. And
  3200. although you may need the functions in Group III only once in a blue moon, you
  3201. should be familiar enough with them that you know how to use them when that
  3202. need arises.
  3203. I certainly don't plan to review the entire library in this article. If you
  3204. want a comprehensive reference, there is nothing better than Plauger's book,
  3205. The Standard C Library [1]. For tips, tricks, and tutorials, you can read most
  3206. of the back issues of this column (which started in October, 1992). What I
  3207. will do here is bring to light some of the library functions you may have
  3208. overlooked, and some behavior you may not be aware of.
  3209.  
  3210.  
  3211. Group I -- For the "Adequate" C Programmer
  3212.  
  3213.  
  3214.  
  3215.  
  3216. <ctype. h>
  3217.  
  3218.  
  3219. The functions in <ctype.h> support typical operations for handling single
  3220. characters (see Table 4). For example, to determine if a character C is upper
  3221. case, use the expression isupper(c). Many old-time C programs are peppered
  3222. with expressions such as
  3223. ('A' <= c && c <= 'Z')
  3224. instead, which makes poor reading. Putting such an expression in a macro
  3225. helps, as in
  3226. #define ISUPPER(c) ('A' <= c & c <= 'Z')
  3227. But this makes expressions with side effects (such as ISUPPER(c++))
  3228. unreliable. And of course this test for uppercase membership works only with a
  3229. character set that encodes the alphabet contiguously, such as ASCII. By
  3230. contrast, the character classification functions in <ctype.h> are safe and
  3231. portable across all platforms.
  3232. It is important not to assume that ASCII is always the execution character
  3233. set. For example, ASCII control characters comprise the code 127 and those
  3234. less than 32, but only seven control characters behave uniformly across all
  3235. environments: alert ( '\a'), backspace ( '\b'), carriage return ( '\r'), form
  3236. feed ( '\f''), horizontal tab ( '\t'), newline ( '\n'), and vertical tab (
  3237. '\v'). The only character-handling functions that do not change behavior when
  3238. you change locale are isdigit and isxdigit.(See Group III next month for more
  3239. on locales).
  3240. Although you can assume that the digits '0' through '9' have contiguous codes
  3241. in all C execution character sets, the hexadecimal digits, being alphabetic
  3242. characters, do not. The function atox in Listing 1 shows how to convert a
  3243. hexadecimal string to an integer value. Unfortunately, it only works for
  3244. ASCII-like character sets. The offending line is:
  3245. digit= toupper(*s) - 'A' + 10;
  3246. There is no guarantee that the expression
  3247. toupper(*s) - 'A'
  3248. will give the correct result. The version in Listing 2 works on any platform
  3249. because it stores all hexadecimal digits contiguously in its own array. It
  3250. searches the array with strchr and then uses pointer arithmetic to compute the
  3251. value of the digit.
  3252.  
  3253.  
  3254. <stdio.h>
  3255.  
  3256.  
  3257. The author of Listing 1 and Listing 2 could have avoided a lot of trouble if
  3258. he had only understood scanf formatting a little better. As Listing 3
  3259. illustrates, the "%x" edit descriptor does all the work of reading hexadecimal
  3260. numbers for you. Unlike the previous two versions, it even handles a leading
  3261. plus or minus sign. Both scanf and printf are laden with features that so many
  3262. programmers overlook. For more detail on these two functions, see the Code
  3263. Capsules in the October 1992 and November 1992 issues of CUJ.
  3264. The printf/scanf families of functions shown in Table 5 perform formatted I/0.
  3265. Furthermore, they provide these facilities for three types of streams:
  3266. standard streams, file streams, and string (i.e., in-core) streams. Formatting
  3267. operates identically on the different types of streams, but of necessity the
  3268. function names and calling sequences are somewhat different.
  3269. The <stdio.h> component of the Standard C library provides two other classes
  3270. of input/output facilities: character I/0 and block I/0 (see Table 6 and Table
  3271. 7). The functions in Listing 4 and Listing 5 copy one file to another using
  3272. character I/0 and block I/0 functions respectively. Note that since fread does
  3273. not return an error code, I must make an explicit call to ferror to detect a
  3274. read error.
  3275. As Table 7 illustrates, <stdio.h> provides functions for file positioning. The
  3276. time-worn functions fseek and ftell work reliably only on files opened in
  3277. binary mode, and are limited to file positions that can be represented by a
  3278. long integer. To overcome these limitations, the ANSI committee invented
  3279. fgetpos and fsetpos, which use the abstract type fpos_t (Table 8) as a file
  3280. position indicator.
  3281. The program in Listing 6 puts fgetpos and fsetpos to good use in a simple
  3282. four-way scrolling browser for large files. The browser keeps only one
  3283. screen's worth of text in memory. If you want to scroll up or down through the
  3284. file, it reads (or re-reads) the adjacent text and displays it. When scrolling
  3285. down (i.e., forward) through the file, the program pushes the file position
  3286. corresponding to the old screen data on a stack, and reads the next screenful
  3287. from the current file position. To scroll up, the program retrieves the file
  3288. position of the previous screen from the stack. For the complete program, and
  3289. for more detailed information about file I/0, see the Code Capsule in the May
  3290. 1993 edition of CUJ.
  3291.  
  3292.  
  3293. <stdlib.h>
  3294.  
  3295.  
  3296. The header <stdlib.h> is a bit of a catch-all. It defines types, macros, and
  3297. functions for memory management, sorting and searching, integer arithmetic,
  3298. string-to-number conversions, sequences of pseudorandom numbers, interfacing
  3299. with the environment, and converting multi-byte strings and characters to and
  3300. from wide character representations (see Table 9). The program in Listing 7
  3301. uses all four memory management functions to sort a text file. Whenever its
  3302. array of pointers to char fills up, it expands it with realloc, which
  3303. preserves the original contents. See "Code Capsules: Dynamic Memory
  3304. Management, Part 1," CUJ, October 1994, for an in-depth treatment of memory
  3305. management in C. For more information on the sort function qsort, see the Code
  3306. Capsule "Sorting with qsort," CUJ, April 1993. The search function, bsearch,
  3307. searches a sorted list for a given key. Like qsort, you supply bsearch with a
  3308. compare function and it returns a pointer to the array element containing the
  3309. key (see Listing 8).
  3310. The program in Listing 9 illustrates some of <stdlib.h>'s seldom-used
  3311. functions. It shuffles a deck of 52 cards by creating a randomized sequence of
  3312. the numbers 0 through 51. The srand function seeds the pseudo-random generator
  3313. by encoding the current time and date. To derive the suit and denomination
  3314. from a number, I divide the number by 13, the number of cards in each suit.
  3315. The remainder of this division is the denomination (0 through 12 corresponding
  3316. to ace through king), and the quotient represents the suit as follows:
  3317.  
  3318. 0 == clubs
  3319. 1 == diamonds
  3320. 2 == hearts
  3321. 3 == spades
  3322. The div function computes the quotient and remainder all at once and stores
  3323. the result in a structure of type div_t..
  3324. The functions in the scanf family call strtol to convert character strings to
  3325. integers. Using strtol directly, however, you can read numbers in any base
  3326. from 2 to 35, as the program in Listing 10 illustrates. strtol updates nextp
  3327. through its second argument so you can progress through the string, converting
  3328. one number after another. The functions strtoul and strtod behave similarly
  3329. for unsigned longs and doubles respectively. With strtol, I can write a
  3330. superior version of the atox conversion function, as shown in Listing 11.
  3331. Everyone reading this column is probably familiar with the functions exit and
  3332. abort. You may not know, however, that you can "register" functions to be
  3333. called automatically at program exit. These functions are usually called "exit
  3334. handlers" and you register them with the atexit function, like this:
  3335. void my_handler();
  3336. atexit(my_handler);
  3337. An exit handler must take no arguments and must return void. Upon normal exit
  3338. (i.e., return from main or a call to exit), C calls all of your handlers in
  3339. the reverse of the order that they were registered. You can register up to 32
  3340. exit handlers.
  3341. The getenv function allows you to query strings in your host environment. For
  3342. example, to find the current setting of the PATH variable, which is common to
  3343. many environments, you can do the following:
  3344. char *path = getenv("PATH");
  3345. The pointer refers to memory outside of your program, so if you want to keep
  3346. the value, you'll have to copy it to a program variable before the next call
  3347. to getenv.
  3348.  
  3349.  
  3350. <string. h>
  3351.  
  3352.  
  3353. The functions defined in <string.h> are shown in Table 10. All the functions
  3354. with the str prefix work on null-terminated strings, and the mem-functions
  3355. process raw memory. You've already seen strchr in Listing 2, and its companion
  3356. memchr in Listing 9. To transfer raw bytes from one location to another, use
  3357. memcpy, or memmove, if the source and destination buffers overlap. (Judging by
  3358. the number of times I've seen memcpy reinvented in others' code, I believe
  3359. that it is the most overlooked function in the standard library).
  3360. The string search functions also go too often unused. The program in Listing
  3361. 12 uses strstr to extract all lines from a text file that contain a given
  3362. string. Due to their cryptic names, the following three <string.h> functions
  3363. are probably the least used:
  3364. 1) size_t strspn(char *s1, char *s 2); "Spans" the characters from s2
  3365. occurring in s1. In plain English, strspn returns the index of the first
  3366. character in s1 which is not in s2.
  3367. 2) size_t strcspn(char *s1, char *s2); "Spans" the characters not in s2
  3368. occurring in s1. In other words, strcspn returns the index of the first
  3369. character in s1 that is also in s2.
  3370. 3) char *strpbrk(char *s1, char *s2); Returns a pointer to the first character
  3371. from s2 that occurs in s1. It's kind of like a cross between strchr and
  3372. strcspn.
  3373. The program in Listing 13 illustrates these functions. For more on string
  3374. handling, see the "Code Capsule" in the December 1992 issue (vol. 10, no. 12).
  3375.  
  3376.  
  3377. Summary
  3378.  
  3379.  
  3380. Although you may not agree totally with my categorization of Standard C
  3381. library components, I hope I have caused you to think about your own practices
  3382. and level of expertise. Whatever our opinions, one bit of advice is
  3383. indisputably in order: know and use the Standard library! Next month I'll
  3384. cover Group II.
  3385.  
  3386.  
  3387. Further Reading
  3388.  
  3389.  
  3390. [1] Plauger, P. J. The Standard C Library. Prentice-Hall, 1992. ISBN
  3391. 0-13-131509-9.
  3392. Table 1 Standard C Headers: Group I (required knowledge for every C
  3393. programmer)
  3394. <ctype.h> Character Handling
  3395.  
  3396. <stdio.h> Input/Output
  3397.  
  3398. <stdlib.h> Miscellaneous Utilities
  3399.  
  3400. <string.h> Text Processing
  3401. Table 2 Standard C Headers: Group II (tools for the professional)
  3402. <assert.h> Assertion Support for Defensive Programming
  3403.  
  3404. <limits.h> System Parameters for Integer Arithmetic
  3405.  
  3406. <stddef.h> Universal Types & Constants
  3407.  
  3408. <time.h> Time Processing
  3409. Table 3 Standard C Headers: Group III (power at your fingertips when you need
  3410. it)
  3411. <errno.h> Error Detection
  3412.  
  3413. <float.h> System Parameters for Real Arithmetic
  3414.  
  3415. <locale.h> Cultural Adaptation
  3416.  
  3417.  
  3418. <math.h> Mathematical Functions
  3419.  
  3420. <setjmp.h> Non-local Branching
  3421.  
  3422. <signal.h> Interrupt Handling (sort of)
  3423.  
  3424. <stdarg.h> Variable-length Argument Lists
  3425. Table 4 <ctype.h> functions
  3426.  Character Testing Functions
  3427. -----------------------------------------------------
  3428. isalnum alphanumeric ( isalpha isdigit)
  3429.  
  3430. isalpha alphabetic
  3431.  
  3432. iscntrl control (beware!)
  3433.  
  3434. isdigit '0' through '9'
  3435.  
  3436. isgraph visible when printed
  3437.  
  3438. islower lower case alphabetic
  3439.  
  3440. isprint isgraph ' '
  3441.  
  3442. ispunct isgraph && !isalnum
  3443.  
  3444. isspace whitespace
  3445.  
  3446. isupper upper case alphabetic
  3447.  
  3448. isxdigit isdigit 'a' thru 'f' 'A' thru 'F'
  3449.  
  3450.  Character Mapping Functions
  3451. -----------------------------------------------------
  3452. tolower convert to lower case (if applicable)
  3453.  
  3454. toupper convert to upper case (if applicable)
  3455. Table 5 Functions for Formatted I/0 defined in <stdio.h>
  3456.  Fixed-length argument lists
  3457. --------------------------------
  3458. scanf (standard input)
  3459.  
  3460. fscanf (file input)
  3461.  
  3462. sscanf (in-core input)
  3463.  
  3464. printf (standard output)
  3465.  
  3466. fprintf (file output)
  3467.  
  3468. sprintf (in-core output)
  3469.  
  3470.  Variable-length argument lists
  3471. ----------------------------------
  3472. vprintf (standard output)
  3473. vfprintf (file output)
  3474. vsprintf (in-core output)
  3475. Table 6 Character I/O Functions in <stdio.h>
  3476. Single-character Processing Functions
  3477.  
  3478. -------------------------------------
  3479. getchar (standard input)
  3480.  
  3481. getc (file input)
  3482.  
  3483. ungetc (affects file input)
  3484.  
  3485. fgetc (file input)
  3486.  
  3487. putchar (standard output)
  3488.  
  3489. putc (file output)
  3490.  
  3491. fputc (file output)
  3492.  
  3493.  String Processing Functions
  3494. -------------------------------------
  3495. gets (standard input)
  3496.  
  3497. fgets (file input)
  3498.  
  3499. puts (standard output)
  3500.  
  3501. fputs (file output)
  3502. Table 7 Other <stdio.h> Functions
  3503.  Block I/0
  3504. -----------------------
  3505.  fread
  3506.  fwrite
  3507.  
  3508. Operations via Filename
  3509. -----------------------
  3510.  remove
  3511.  rename
  3512.  
  3513.  Temporary Files
  3514. -----------------------
  3515.  tmpfile
  3516.  tmpnam
  3517.  
  3518.  File Access Functions
  3519. -----------------------
  3520.  fopen
  3521.  freopen
  3522.  fclose
  3523.  fflush
  3524.  setbuf
  3525.  setvbuf
  3526.  
  3527.  File Positioning
  3528. -----------------------
  3529.  fgetpos
  3530.  fsetpos
  3531.  fseek
  3532.  ftell
  3533.  rewind
  3534.  
  3535.  Error Handling
  3536. -----------------------
  3537.  
  3538.  clearerr
  3539.  feof
  3540.  ferror
  3541. Table 8 Types and macros defined in <stdio.h>
  3542.  Types
  3543. ------------------------------------------------------------
  3544. FILE Encapsulates file access info
  3545. fpos_t File Position (returned by fgetpos)
  3546.  
  3547.  Macros
  3548. ------------------------------------------------------------
  3549. NULL Zero pointer
  3550. EOF Special value representing end-of-file
  3551. BUFSIZ Preferred stream buffer size
  3552. FOPEN_MAX Max # of files open simultaneously
  3553. FILENAME_MAX Max # of characters in a file name minus 1
  3554. L_tmpnam Max # of characters in a tempfile name minus 1
  3555. TMP_MAX Max # of distinct filenames returned from
  3556.  tmpnam
  3557. SEEK_CUR Signals fseek to seek relative to current position
  3558. SEEK_END Signals fseek to seek from end-of-file
  3559. SEEK_SET Signals fseek to seek from start-of-file
  3560. Table 9 <stdlib.h> Declarations
  3561.  Types
  3562. --------------------------------------------------------
  3563. div_t (structure returned by div)
  3564. ldiv_t (structure returned by ldiv)
  3565.  
  3566.  Constants
  3567. --------------------------------------------------------
  3568. NULL
  3569. EXIT_FAILURE (Portable error code for exit)
  3570. EXIT_SUCCESS (Portable success code for exit)
  3571. RAND_MAX (Max value returned by rand)
  3572. MB_CUR_MAX (Max # of bytes in a multi-byte character)
  3573.  
  3574.  String Conversion Functions
  3575. --------------------------------------------------------
  3576. atof strtod
  3577. atoi strtol
  3578. atol strtoul
  3579.  
  3580.  Random number Functions
  3581. --------------------------------------------------------
  3582. rand (Returns the next pseudo-random number)
  3583. srand ("Seeds" the sequence of pseudo-random
  3584.  numbers)
  3585.  
  3586.  Memory Management
  3587. --------------------------------------------------------
  3588. calloc realloc
  3589. malloc free
  3590.  
  3591.  Interface to the Environment
  3592. --------------------------------------------------------
  3593. abort getenv
  3594. atexit system
  3595. exit
  3596.  
  3597.  
  3598.  Searching and Sorting
  3599. --------------------------------------------------------
  3600. bsearch
  3601. qsort
  3602.  
  3603.  Integer Arithmetic
  3604. --------------------------------------------------------
  3605. abs labs
  3606. div ldiv
  3607.  
  3608.  Multibyte Character Functions
  3609. --------------------------------------------------------
  3610. mblen mbctowcs
  3611. mbtowc wcstombs
  3612. wctomb
  3613. Table 10 Functions defined in <string.h>
  3614.  Copying
  3615. ------------------------------------
  3616. memcpy strcpy
  3617. memmove strncpy
  3618.  
  3619.  Concatenation
  3620. ------------------------------------
  3621. strcat
  3622. strncat
  3623.  
  3624.  Comparison
  3625. ------------------------------------
  3626. memcmp strncmp
  3627. strcmp strxfrm
  3628. strcoll
  3629.  
  3630.  Search Functions
  3631. ------------------------------------
  3632. memchr strrchr
  3633. strchr strspn
  3634. strcspn strstr
  3635. strpbrk strtok
  3636.  
  3637.  Miscellaneous
  3638. ------------------------------------
  3639. memset
  3640. strerror
  3641. strlen
  3642.  
  3643. Listing 1 Converts a hex-string to a number in ASCII environments
  3644. #include <ctype.h>
  3645. #include <assert.h>
  3646.  
  3647. long atox(char *s)
  3648. {
  3649. long sum;
  3650.  
  3651. assert(s);
  3652.  
  3653. /* Skip whitespace */
  3654. while (isspace(*s))
  3655. ++s;
  3656.  
  3657.  
  3658. /* Do the conversion */
  3659.  
  3660. for (sum = 0L; isxdigit(*s); ++s)
  3661. {
  3662. int digit;
  3663.  
  3664. if (isdigit(*s))
  3665. digit = *s - '0';
  3666. else
  3667. digit = toupper(*s) - 'A' + 10;
  3668. sum = sum*16L + digit;
  3669. }
  3670.  
  3671. return sum;
  3672. }
  3673. /* End of File */
  3674.  
  3675.  
  3676. Listing 2 A portable version of Listing 1
  3677. #include (ctype.h>
  3678. #include <assert.h>
  3679. #include <string.h>
  3680.  
  3681. long atox(char *s)
  3682. {
  3683. char xdigs[] = "012345679ABCDEF":
  3684. long sum;
  3685.  
  3686. assert(s);
  3687.  
  3688. /* Skip whitespace */
  3689. while (isspace(*s))
  3690. ++s;
  3691.  
  3692. /* Do the conversion */
  3693. for (sum = 0L: isxdigit(*s); ++s)
  3694. {
  3695. int digit = strchr(xdigs,toupper(*s)) - xdigs;
  3696. sum = sum*16L + digit;
  3697. }
  3698.  
  3699. return sum;
  3700. }
  3701. /* End of File */
  3702.  
  3703.  
  3704. Listing 3 Converts a hex-string to a number via sscanf
  3705. #include <stdio.h>
  3706.  
  3707. long atox(char *s)
  3708. {
  3709. long n = 0L;
  3710. sscanf( s, "%x", &n );
  3711. return n;
  3712. }
  3713.  
  3714. /* End of File */
  3715.  
  3716.  
  3717.  
  3718. Listing 4 A Function that copies a file via character I/0
  3719. /* copy1.c */
  3720. #include <stdio.h>
  3721.  
  3722. int copy(FILE *dest, FILE *source)
  3723. {
  3724. int c;
  3725.  
  3726. while ((c = getc(source)) != EOF)
  3727. if (putc(c,dest) == EOF)
  3728. return EOF;
  3729. return 0;
  3730. }
  3731.  
  3732. /* End of File */
  3733.  
  3734.  
  3735. Listing 5 A Function that copies a file via block I/0
  3736. /* copy2.c */
  3737. #include <stdio.h>
  3738.  
  3739. int copy(FILE *dest, FILE *source)
  3740. {
  3741. size_t count;
  3742. static char buf[BUFSIZ];
  3743.  
  3744. while (!feof(source))
  3745. {
  3746. count = fread(buf,l,BUFSIZ,source);
  3747. if (ferror(source))
  3748. return EOF;
  3749. If (fwrite(buf,l,count,dest) != count)
  3750. return EOF;
  3751.  
  3752. }
  3753. return 0;
  3754. }
  3755.  
  3756. /* End of File */
  3757.  
  3758.  
  3759. Listing 6 Outline of a file viewing program that illustrates file positioning
  3760. /* view.c: A simple 4-way-scrolling file browser */
  3761.  
  3762. /* cls(), display(), read_a_screen() omitted...*/
  3763.  
  3764. main(int argc, char *argv[])
  3765. {
  3766. fpos_t top_pos, stk_[MAXSTACK];
  3767. /* Details omitted...*/
  3768.  
  3769. top:
  3770. /* Display initial screen */
  3771. rewind(f);
  3772. fgetpos(f,&top_pos);
  3773. /* Details omitted...*/
  3774.  
  3775. for (;;)
  3776. {
  3777.  
  3778. switch(c = toupper(getchar()))
  3779. {
  3780. case 'D': /* Display the next screen */
  3781. if (!feof(f))
  3782. {
  3783. PUSH(top_pos);
  3784. fgetpos(f,&top_pos);
  3785. read_a_screeen(f);
  3786. display(file);
  3787. }
  3788. break;
  3789.  
  3790. case 'U': /* Display the previous screen */
  3791. if (stkptr_> 0)
  3792. {
  3793. top_pos = POP();
  3794. fsetpos(f,&top_pos);
  3795. read_a_screen(f);
  3796. display(file);
  3797. }
  3798. break;
  3799. case 'T': /* Display last screen */
  3800. stkptr_ = 0;
  3801. goto top;
  3802.  
  3803. case 'B': /* Display last screen */
  3804. while (!feof(f)
  3805. {
  3806. PUSH(top_pos);
  3807. fgetpos(f,&top_pos);
  3808. read_a_screen(f)
  3809. }
  3810. display(file);
  3811. break;
  3812.  
  3813. case 'Q': /* Quit */
  3814. cls();
  3815. return EXIT_SUCCESS;
  3816. }
  3817. /* Details omitted...*/
  3818. }
  3819. }
  3820. /* End of file */
  3821.  
  3822.  
  3823. Listing 7 Sorts files as large as available memory
  3824. /* sort.c */
  3825. #include <stdio.h>
  3826. #include <assert.h>
  3827. #include <stdlib.h>
  3828. #include <string.h>
  3829.  
  3830. #define MAXLINES 512
  3831.  
  3832. int comp(const void *, const void *);
  3833.  
  3834. main()
  3835. {
  3836. int i;
  3837.  
  3838. size_t nlines, maxlines = MAXLINES;
  3839. static char s[BUFSIZ];
  3840. char **lines = calloc(sizeof(char *), maxlines);
  3841.  
  3842. /* Read file */
  3843. for (nlines = 0; fgets(s,BUFSIZ,stdin); ++nlines)
  3844. {
  3845. if (nlines == maxlines)
  3846. {
  3847. /* Expand array of strings */
  3848. maxlines += MAXLINES;
  3849. lines = realloc(lines,maxlines*sizeof(char *));
  3850. assert(lines);
  3851. }
  3852.  
  3853. /* Store this line */
  3854. lines[nlines] = malloc(strlen(s)+1);
  3855. assert(lines[nlines]);
  3856. strcpy(lines[nlines],s);
  3857. }
  3858.  
  3859. /* Sort */
  3860. qsort(lines,nlines,sizeof lines[0],comp);
  3861.  
  3862. /* Print / free memory */
  3863. for (i = 0; i < nlines; ++i)
  3864. {
  3865. fputs(lines[i],stdout);
  3866. fflush(stdout);
  3867. assert(!ferror(stdout));
  3868. free(lines[i]);
  3869. }
  3870. free(lines);
  3871. return 0;
  3872. }
  3873.  
  3874. /* Compare function for qsort(): */
  3875. int comp(const void *pl, const void *p2)
  3876. {
  3877. return strcmp(* (char **) pl, * (char **) p2);
  3878. }
  3879.  
  3880. /* End of File */
  3881.  
  3882.  
  3883. Listing 8 Searches a sorted array of records with the bsearch function
  3884. /* search.c */
  3885. #include <stdio.h>
  3886. #include <stdlib.h>
  3887. #include <string.h>
  3888.  
  3889. struct person
  3890. {
  3891. char last[16];
  3892. char first[11];
  3893. char phone[13];
  3894. int age;
  3895. };
  3896.  
  3897.  
  3898. static int comp(const void *, const void *);
  3899.  
  3900. main()
  3901. {
  3902. int i;
  3903. struct person *p;
  3904. static struct person key = {"","","555-1965",0};
  3905. static struct person people[] =
  3906. {{"Ford","Henry","555-1903",98},
  3907. {"Lincoln","Abraham","555-1865",161},
  3908. {"Ford","Edsel","555-1965",53},
  3909. {"Trump","Donald","555-1988",49}};
  3910.  
  3911. /* Sort */
  3912. qsort(people, 4, sizeof people[0], comp);
  3913.  
  3914. /* Search */
  3915. p = bsearch(&key, people, 4, sizeof people[0], comp);
  3916. if (p != NULL)
  3917. {
  3918. printf(
  3919. "%s, %s, %s, %d\n",
  3920. p->last,
  3921. p->first,
  3922. p->phone,
  3923. p->age
  3924. );
  3925. }
  3926. else
  3927. puts("Not found");
  3928. return 0;
  3929. }
  3930.  
  3931. /* Compare function: */
  3932. static int comp(const void *x, const void *y)
  3933. {
  3934. struct person *pl = (struct person *) x;
  3935. struct person *p2 = (struct person *) y;
  3936.  
  3937. return strcmp(p1->phone,p2->phone);
  3938. }
  3939.  
  3940. /* Output: */
  3941. Ford, Edsel, 555-1965, 53
  3942.  
  3943. /* End of File */
  3944.  
  3945.  
  3946. Listing 9 A card shuffling program that illustrates the random number and
  3947. integer division functions in <stdlib.h>
  3948. /* deal.c: Deal a hand from a shuffled deck of cards */
  3949. #include <stdio.h>
  3950. #include <stdlib.h>
  3951. #include <string.h>
  3952. #include <time.h>
  3953.  
  3954. #define DECKSIZE 52
  3955. #define SUITSIZE 13
  3956.  
  3957. main(int argc, char *argv[])
  3958.  
  3959. {
  3960. int ncards = DECKSIZE; /* Deal full deck by default */
  3961. char deck[DECKSIZE]; /* An array of small integers */
  3962. size_t deckp;
  3963. unsigned int seed;
  3964.  
  3965. /* Get optional hand size */
  3966. if (argc > 1)
  3967. if ((ncards = abs(atoi(argv[1])) % DECKSIZE) == 0)
  3968. ncards = DECKSIZE;
  3969.  
  3970. /* Seed the random number generator */
  3971. seed = (unsigned int) time(NULL);
  3972. srand(seed);
  3973.  
  3974. /* Shuffle */
  3975. deckp = 0;
  3976. while (deckp < ncards)
  3977. {
  3978. int num = rand() % DECKSIZE;
  3979. if (memchr(deck, num, deckp) == NULL)
  3980. deck[deckp++] = (char) num;
  3981. }
  3982.  
  3983. /* Deal */
  3984. for (deckp = 0; deckp < ncards; ++deckp)
  3985. {
  3986. divt_card = div(deck[deckp], SUITSIZE);
  3987. printf(
  3988. "%c(%c)%c",
  3989. "A23456789TJQK"[card.rem],
  3990. "CDHS" [card.quot],
  3991. (deckp+1) % SUITSIZE ? ' ' : '\n'
  3992. );
  3993. }
  3994.  
  3995. return 0;
  3996. }
  3997.  
  3998. /* Output: */
  3999. A(C) 6(S) 7(C) 9(C) 3(H) 6(C) 8(D) 3(C) 6(D) 5(D) 2(H) A(S) 4(H)
  4000. 8(C) 8(H) 6(H) J(S) 7(S) Q(C) 2(C) Q(H) K(H) 4(C) 5(S) T(H) Q(S)
  4001. 9(H) T(D) T(S) 9(D) K(C) 3(S) J(C) 5(C) T(C) K(S) 7(D) 2(D) 4(S)
  4002. 8(S) 5(H) A(D) 7(H) 3(D) Q(D) A(H) 2(S) J(D) 9(S) K(D) J(H) 4(D)
  4003.  
  4004. /* End of File */
  4005.  
  4006.  
  4007. Listing 10 Uses strtol() to read numbers in different bases
  4008. #include <stdio.h>
  4009. #include <stdlib.h>
  4010.  
  4011. main()
  4012. {
  4013. char *input = "101 123 45678 90abc g";
  4014. char *nextp = input;
  4015. long bin, oct, dec, hex, beyond:
  4016.  
  4017. bin = strtol(nextp,&nextp,2);
  4018.  
  4019. oct = strtol(nextp,&nextp,8);
  4020. dec = strtol(nextp,&nextp,10);
  4021. hex = strtol(nextp,&nextp,16);
  4022. beyond = strtol(nextp,&nextp,17);
  4023.  
  4024. printf("bin = %ld\n",bin);
  4025. printf("oct =
  4026. %lo\n",oct);
  4027. printf("dec = %ld\n",dec);
  4028. printf("hex = %lx\n",hex);
  4029. printf("beyond = %ld\n",beyond);
  4030. return 0;
  4031. }
  4032.  
  4033. /* Output: */
  4034. bin = 5
  4035. oct = 123
  4036. dec = 45678
  4037. hex = 90abc
  4038. beyond = 16
  4039.  
  4040. /* End of File */
  4041.  
  4042.  
  4043. Listing 11 An even better version of atox() using strtol()
  4044. #include <stdlib.h>
  4045.  
  4046. long atox(char *s)
  4047. {
  4048. return strtol(s, NULL, 16);
  4049. }
  4050.  
  4051. /* End of File */
  4052.  
  4053.  
  4054. Listing 12 Uses strstr to find substrings
  4055. /* find.c: Extract lines from a file */
  4056.  
  4057. #include <stdio.h>
  4058. #include <stdlib.h>
  4059. #include <string.h>
  4060.  
  4061. main(int argc, char *argv[])
  4062. {
  4063. char line[BUFSIZ];
  4064. char *search_str;
  4065. int lineno = 0;
  4066.  
  4067. if (argc == 1)
  4068. return EXIT_FAILURE;
  4069. /* Search string required */
  4070. else
  4071. search_str = argv[l];
  4072.  
  4073. while (gets(line))
  4074. {
  4075. ++lineno;
  4076. if (strstr(line,search_str))
  4077. printf("%d: %s\n",lineno,line);
  4078.  
  4079. }
  4080.  
  4081. return EXIT_SUCCESS;
  4082. }
  4083.  
  4084. /*Results from the command
  4085. "find str <find.c": */
  4086. 5: #include <string.h>
  4087. 12: char *search_str;
  4088. 16: return EXIT_FAILURE;
  4089. 18: search_str = argv[l];
  4090. 23: if (strstr(line,search_str))
  4091.  
  4092. /* End of File */
  4093.  
  4094.  
  4095. Listing 13 Illustrates selected string search functions
  4096. #include <stdio.h>
  4097. #include <string.h)
  4098.  
  4099. void display_span(char *, int);
  4100. main()
  4101. {
  4102. char *s = "Eeek! A mouse device!;
  4103. char *vowels = "AEIOUaeiou";
  4104. char *punct =
  4105. "'~!@#$%^&*()-_=+\\[{]};:'\",<.>/?";
  4106. char *ptr;
  4107.  
  4108. display_span(s,strspn(s,vowels));
  4109. display_span(s,strspn(s,punct));
  4110. display_span(s,strcspn(s,vowels));
  4111. display_span(s,strcspn(s,punct));
  4112.  
  4113. ptr = strpbrk(s,vowels);
  4114. puts(ptr);
  4115. ptr = strpbrk(s,punct);
  4116. puts(ptr);
  4117.  
  4118. return 0;
  4119. }
  4120.  
  4121. void display_span(char *s, size_t index)
  4122. {
  4123. printf("%d characters spanned: %.*s\n",
  4124. index,index,s);
  4125. }
  4126.  
  4127. /* Output: */
  4128. 3 characters spanned: Eee
  4129. 0 characters spanned:
  4130. 0 characters spanned:
  4131. 4 characters spanned: Eeek
  4132. Eeek! A mouse device!
  4133. ! A mouse device!
  4134.  
  4135. /* End of File */
  4136.  
  4137.  
  4138.  
  4139.  
  4140.  
  4141.  
  4142.  
  4143.  
  4144.  
  4145.  
  4146.  
  4147.  
  4148.  
  4149.  
  4150.  
  4151.  
  4152.  
  4153.  
  4154.  
  4155.  
  4156.  
  4157.  
  4158.  
  4159.  
  4160.  
  4161.  
  4162.  
  4163.  
  4164.  
  4165.  
  4166.  
  4167.  
  4168.  
  4169.  
  4170.  
  4171.  
  4172.  
  4173.  
  4174.  
  4175.  
  4176.  
  4177.  
  4178.  
  4179.  
  4180.  
  4181.  
  4182.  
  4183.  
  4184.  
  4185.  
  4186.  
  4187.  
  4188.  
  4189.  
  4190.  
  4191.  
  4192.  
  4193.  
  4194.  
  4195.  
  4196.  
  4197.  
  4198.  
  4199.  
  4200.  
  4201.  
  4202. Stepping Up To C++
  4203.  
  4204.  
  4205. C++ at CD Registration
  4206.  
  4207.  
  4208.  
  4209.  
  4210. Dan Saks
  4211.  
  4212.  
  4213. Dan Saks is the president of Saks & Associates, which offers consulting and
  4214. training in C++ and C. He is secretary of the ANSI and ISO C++ committees. Dan
  4215. is coauthor of C++ Programming Guidelines, and codeveloper of the Plum Hall
  4216. Validation Suite for C++ (both with Thomas Plum). You can reach him at 393
  4217. Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601, or
  4218. electronically at dsaks@wittenberg.edu.
  4219.  
  4220.  
  4221. As P.J. Plauger recently reported in his "Editor's Forum," CUJ, September
  4222. 1994, the draft C++ standard has reached a major milestone on its way to
  4223. becoming a bona fide standard. This past July, the joint ANSI and ISO C++
  4224. standards committees voted to submit the latest draft for registration as a
  4225. committee draft (CD). CD registration is the first of three ballots that a
  4226. draft must pass before it becomes an international standard (IS).
  4227. As Plauger explained, the significance of this stage (aside from simply
  4228. showing progress) is that it's supposed to be the point at which all the key
  4229. features of the language are in place. As such, this is probably a good time
  4230. to look back and see how the language has changed over the past few years.
  4231.  
  4232.  
  4233. Who's Standardizing What?
  4234.  
  4235.  
  4236. It's hard to talk about programming language standards without bandying about
  4237. acronyms like ANSI and ISO, or arcane codenames like X3J16 or WG21. Although
  4238. you clearly don't need to understand these terms to program competently in
  4239. C++, I expect that many of you are at least a little interested in
  4240. understanding the forces that shape your destiny.
  4241. Table 1 contains a handy quick reference guide to standards organizations and
  4242. committees affecting C and C++. You may find it helptul to refer to the table
  4243. from time to time.
  4244. ANSI (pronounced "AN-see") stands for the American National Standards
  4245. Institute. ANSI is a trade association that sets standards for industrial
  4246. products and processes. ANSI standards cover a wide assortment of products
  4247. such as bar codes, bicycle helmets, heating and air conditioning equipment,
  4248. and plywood. ANSI is not a US government agency and its standards are not in
  4249. and of themselves binding by law. However, a government agency may adopt an
  4250. ANSI standard for regulatory or procurement purposes, thus entwining that
  4251. standard with laws.
  4252. ANSI doesn't actually write standards; it establishes procedures for writing
  4253. and approving standards, and then delegates most of the work to
  4254. industry-specific standards committees. X3 is the ANSI-accredited committee
  4255. that administers standards development for information processing systems
  4256. (which includes programming languages). X3 operates out of the headquarters of
  4257. CBEMA, the Computer and Business Equipment Manufacturer's Association. CBEMA
  4258. (pronounced "see-BEE-ma") provides funding and staffing for X3's activities.
  4259. X3 in turn establishes technical committees to develop specific standards. X3
  4260. has chartered, among others, X3J11 and X3J16 to develop the ANSI standards for
  4261. C and C++, respectively. X3J16 started working in early 1990, and has been
  4262. meeting three times a year ever since.
  4263. ANSI has counterparts in other nations, such as AFNOR (France), BSI (UK), DIN
  4264. (Germany), JTSC (Japan), SCC (Canada), and many others. (To the rest of the
  4265. world, please pardon me for not listing your national standards body.) These
  4266. national standards bodies are members of ISO ("I-so"), the International
  4267. Organization for Standardization. As with its members, ISO doesn't write
  4268. standards, but rather charters committees to establish standards according to
  4269. ISO procedures. ISO standards are not laws, but they have become the basis for
  4270. international treaties, and so should not be taken lightly.
  4271. ISO works cooperatively with yet another standards group, IEC, the
  4272. International Electrotechnical Commission. ISO and IEC formed a joint
  4273. technical committee, JTC1, to standardize information technology. JTC1's
  4274. subcommittee SC22 oversees the development of international programming
  4275. language standards.
  4276. The international counterpart of an ANSI technical subcommittee like X3J16 is
  4277. called a "working group." WG14 and WG21 are the SC22 working groups
  4278. responsible for the international C and c++ standards, respectively. (WG21's
  4279. fully-qualified name is ISO/IEC JTC1/SC22/WG21.)
  4280. WG21 has been meeting jointly with X3J16 since the summer of 1991. The joint
  4281. committees are working to produce a single document that, if all goes well,
  4282. will become both the ANSI and ISO standards for C++. I often refer to the
  4283. joint committees as a singular entity (the "joint committee" with no "s") or
  4284. as WG21+X3J16.
  4285.  
  4286.  
  4287. Base Documents
  4288.  
  4289.  
  4290. Rather than write the C++ standard from scratch, X3J16 started by adopting the
  4291. AT&T C++ Product Reference Manual (the PRM) version 2.1 as the base document,
  4292. that is, as the initial working draft. C++ programmers are not as familiar
  4293. with the PRM as they are with the Annotated C++ Reference Manual (the ARM)[1].
  4294. The ARM is a textbook that includes a version of the PRM along with
  4295. annotations and commentary that elaborate the language description, explain
  4296. many language design decisions, and suggest implementation techniques. The ARM
  4297. also includes chapters on templates and exceptions, which the PRM does not.
  4298. Thus, strictly speaking, the ARM is not the base document. However, X3J16's
  4299. first decisions eliminated most of the differences between the draft and the
  4300. ARM (aside from the annotations and commentary). Therefore, for all practical
  4301. purposes, the initial draft standard was based on the ARM, and so it's fair to
  4302. call the ARM the base document.
  4303. X3J16 selected the C standard as its second base document. Over the years, the
  4304. committee has mined the C standard for passages describing parts of C that are
  4305. supposed to be the same, or nearly the same, in C++. For example, the ARM does
  4306. not include a grammar for the lexical tokens of C++ (constructs such as
  4307. identifiers and literals). The C++ draft standard now incorporates the lexical
  4308. grammar from the C standard. Similarly, the C++ draft's description of the
  4309. preprocessor is now nearly identical to the one in the C standard.
  4310. Nonetheless, the overall structure of the C++ draft still largely resembles
  4311. that of the ARM.
  4312. Occasionally, I receive requests for information about or copies of the "C++
  4313. 3.0 standard" or something similar. There is no such thing. I think such
  4314. questions arise because before there was a draft standard, compiler vendors
  4315. used to claim compatibility with numbered versions of AT&T's C++ compiler.
  4316. C++, like C, began at AT&T Bell Labs. AT&T started distributing a C++ compiler
  4317. called cfront in the mid-1980s. For a few years, it was the only C++ compiler
  4318. around. For several more years, cfront remained the de facto standard for C++
  4319. compilers. Vendors of the first non-AT&T C++ compilers typically described the
  4320. features their compilers supported by comparison with AT&T's product. This
  4321. practice continued through the release of cfront 3.0 in 1991, even after X3J16
  4322. had been at work for a couple of years. Comparisons with cfront 3.0 are now
  4323. passé, but the confusion apparently still lingers.
  4324. In short, there is not yet a C++ standard, but there is a draft C++ standard
  4325. inching its way toward formal acceptance several years from now. That draft is
  4326. based on, and still bears a strong resemblance to, the ARM, but it has also
  4327. gone well beyond the ARM in many ways. What follows is a summary of the
  4328. changes in the language wrought by standardization. But first...
  4329.  
  4330.  
  4331. What About Libraries?
  4332.  
  4333.  
  4334. Except for mentioning a few header files and functions, the ARM does not
  4335. describe any run-time library accompanying a C++ implementation. From the
  4336. beginning, X3J16 agreed that any acceptable C++ language standard must include
  4337. a library. The current C++ draft includes a fairly extensive library,
  4338. including language support functions, iostreams, complex arithmetic, and an
  4339. adaption of the Standard C library. The draft also contains a variety of data
  4340. structures, such as strings, dynamic arrays, and assorted container classes,
  4341. most of which are provided as templates.
  4342. I shall focus my attentions on the language itself, and defer to my colleagues
  4343. at CUJ in describing the C++ library. (See Chuck Allison's column, "Code
  4344. Capsules: The Standard C++ Library," CUJ, December 1994.) You can find even
  4345. greater detail on the library in P.J. Plauger's CUJ columns over the last year
  4346. and well into the future, or in his most recent book [2].
  4347.  
  4348.  
  4349. Language Changes
  4350.  
  4351.  
  4352. The joint committee has made a lot of changes in the C++ language definition.
  4353. Many of the changes are substantive; they change the syntactic structure
  4354. and/or the semantic interpretation of the C++ language itself. Other changes
  4355. are just editorial -- they are changes in the description of the C++ language,
  4356. not in the C++ language itself. The following lists omit changes that I
  4357. believe are purely editorial.
  4358. To give you a better sense of the nature of the substantive changes, I've
  4359. grouped them into four broad categories:
  4360. major extensions that dramatically increase the complexity of the language,
  4361. and support alternative programming styles and paradigms
  4362. minor enhancements that extend C++ in less dramatic ways
  4363.  
  4364. changes that alter the meaning of existing features
  4365. clarifications of existing features
  4366. This is not a hard-and-fast classification scheme, and my judgments about what
  4367. goes where are admittedly subjective. But hey, it's my column.
  4368.  
  4369.  
  4370. Major Extensions
  4371.  
  4372.  
  4373. The major extensions are:
  4374. Templates
  4375. Exception handling
  4376. Run-time type information (including dynamic_cast)
  4377. Namespaces
  4378. Templates are a translation-time facility for writing generic functions and
  4379. classes in terms of unspecified types. For example,
  4380. template <class T>
  4381. void swap(T &a, T &b)
  4382. {
  4383. T t = a;
  4384. a = b;
  4385. b = t;
  4386. }
  4387. defines a template for a function that will swap (exchange the values stored
  4388. in) two objects of arbitrary type T. I described function templates in some
  4389. detail in "Recent Extensions to C++," CUJ, June 1993. I devoted my entire
  4390. column last month to template classes ("Designing Generic Container Classes,
  4391. Part 6: Templates," CUJ, December 1994). You will find a lengthy discussion
  4392. and numerous examples of templates in [3] and [4]
  4393. Exception handling is a facility for orderly recovery from exceptional
  4394. (typically erroneous) events during program execution. C++ exception handlers
  4395. can only handle synchronous events -- the kinds of events that can be
  4396. expressed as conditional expressions (for use in if or while statements or in
  4397. assert macro calls). Exception handlers cannot intercept asynchronous events,
  4398. such as device interrupts or hardware faults, which are better handled as
  4399. signals.
  4400. C++ provides exception handling through additional flow structures and library
  4401. functions. A try-block is a compound statement (a sequence of statements
  4402. enclosed in brackets) followed by a sequence of one or more handlers, also
  4403. known as catch clauses. For example,
  4404. try
  4405. {
  4406. // do something
  4407. }
  4408. catch (const char *s)
  4409. {
  4410. // catch an error
  4411. }
  4412. is a try-block. The handlers "catch" exceptions "thrown" by throw expressions
  4413. executed in the compound statement or in functions called from within the
  4414. compound statement, For example, executing
  4415. throw "something happened";
  4416. throws an exception that will be caught by the catch clause above. The throw
  4417. terminates every active function invoked from the try-block and transfers
  4418. control to the catch clause. Presumably, the catch clause handles the
  4419. exception somehow.
  4420. For much more detail on exceptions see Chuck Allison's "Code Capsules: C++
  4421. Exceptions," CUJ, July 1994, as well as [3] and [4].
  4422. Run-time type information (RTTI) is a facility for querying the dynamic type
  4423. of a polymorphic object. A polymorphic object is an object whose class has at
  4424. least one virtual function. An expression that refers to a polymorphic object
  4425. has both a static type and a dynamic type, which may be different. For
  4426. example, given:
  4427. class B
  4428. {
  4429. public:
  4430. virtual void f();
  4431. ...
  4432. };
  4433. class D: public B
  4434. {
  4435. ...
  4436. };
  4437. B*pb = new D;
  4438. then *pb is a polymorphic object with static (declared) type B but dynamic
  4439. (run-time) type D.
  4440. Using RTTI, you can query an object with a given static type to determine if
  4441. it has a particular dynamic type. One form of query is an expression such as
  4442. if (typeid(*pb) == typeid(D))
  4443. ...
  4444. You can also try to convert pb to a D * using
  4445. D *pd = dynamic_cast<D *>(pb);
  4446. which sets pd to 0 (a null pointer) if pb does not point to an object whose
  4447. dynamic type is either D or a type derived from D.
  4448. For a little more detail on RTTI see Chuck Allison's "Code Capsules:
  4449. Conversions and Casts," CUJ, September 1994. For much more detail, see [4].
  4450. Namespaces offer a mechanism for adding qualifiers to global names in the hope
  4451. of reducing global name conflicts. Global name conflicts typically occur when
  4452. a program attempts to use two different libraries that use the same global
  4453. name but for different purposes. For example, libA.h might declare
  4454. class status { ... };
  4455. but libB.h might declare
  4456.  
  4457. enum status { ... };
  4458. A translation unit that tries to include both libA.h and libB.h will run up
  4459. against compilation errors.
  4460. Wrapping these global names in separate namespaces eliminates the conflicts.
  4461. In libA.h, you write:
  4462. namespace A
  4463. {
  4464. class status
  4465. { ... };
  4466. ...
  4467. };
  4468. and in libB.h you write:
  4469. namespace B
  4470. {
  4471. enum status { ... };
  4472. ...
  4473. };
  4474. A program that uses both libraries must refer to a status type by its
  4475. explicitly-qualified name, i.e., as either A::status or B::status. If you
  4476. would like the unqualified name status to mean A::status by default, you can
  4477. write
  4478. using namespace A;
  4479. Then you must still refer to B::status by its fully-qualified name. Writing
  4480. both
  4481. using namespace A;
  4482. using namespace B;
  4483. reintroduces the name conflict.
  4484. Chuck Allison's "Code Capsules: Visibility in C++," CUJ, May 1994 has more
  4485. discussion of namespaces. [4] has even more.
  4486.  
  4487.  
  4488. Minor Enhancements
  4489.  
  4490.  
  4491. In addition to the major extensions, the draft C++ standard includes numerous
  4492. minor enhancements:
  4493. New keywords and digraphs as alternate ISO646-compliant spellings for their
  4494. corresponding tokens
  4495. Operator overloading on enumerations
  4496. operator new[] and operator delete[]
  4497. Relaxed restrictions on the return type of virtual functions
  4498. wchar_t as a keyword representing a distinct type
  4499. A Boolean type, bool
  4500. Declarations in conditional expressions
  4501. New cast notation
  4502. Qualified names in elaborated-type specifiers
  4503. Expressions of the form a.::B::c and p->::B::c (that is, with :: after the .
  4504. or ->)
  4505. Conversion from T **to const T *const *
  4506. Layout rules for POD-struct and POD-union (POD-struct means "plain old data
  4507. structure")
  4508. Mutable class members
  4509. Compile-time member constants
  4510. Default return value (of 0) from main
  4511. Empty initializer-clauses
  4512. I covered the first five of these minor enhancements in "Recent Extensions to
  4513. C++," CUJ, June 1993. [4] covers some the others. I'll get around to covering
  4514. all of them in an upcoming column.
  4515. All of the extensions and enhancements have added a lot of keywords to C++.
  4516. See Table 2 for the complete list of C++ keywords, as listed in the current
  4517. draft standard.
  4518.  
  4519.  
  4520. Changes in Meaning
  4521.  
  4522.  
  4523. The C++ draft also changes the behavior of numerous constructs from their
  4524. previous behaviors specified by the ARM. Some changes simply prohibit features
  4525. that were never intended to be, yet somehow slipped into some implementations.
  4526. Other changes actually just change some constructs with explicitly-defined
  4527. behavior to have different behavior. Roll with it.
  4528. Here are the changes:
  4529. Conditional statements introduce a new block scope.
  4530. Enumerations are no longer integral types.
  4531. The rules for class scopes have greater consistency.
  4532. Friend function definitions in local classes are prohibited.
  4533. Conversions between pointer to object type and pointer to function are
  4534. prohibited.
  4535. Operator op is not a valid identifier name except when used as a function
  4536. name.
  4537.  
  4538. cv-qualifiers (const and volatile) are ignored when modifying parameter types
  4539. in function types.
  4540. Parameter types that include pointer to array of unknown bound of T are
  4541. prohibited.
  4542. The lifetime of a compiler-generated temporary object lasts to the end of the
  4543. full-expression in which the temporary is created.
  4544. An enumerator may be accessed with a. or -) operators.
  4545. Omitting the type specifiers (and implying int) is (1) prohibited in
  4546. declarations wherever Standard C prohibits it, as well as (2) prohibited in a
  4547. typedef without a type-specifier (e.g. typedef I;), and (3) deprecated in all
  4548. other contexts.
  4549. The scope of a declaration in a for-init-statement is restricted to the
  4550. for-statement.
  4551. The left hand side of a . or -> operator is always evaluated.
  4552. A derived class constructor may explicitly initialize an inherited virtual
  4553. base.
  4554. T() has a specified value for every type T.
  4555. The point of declaration for an enumerator is immediately after its
  4556. enumerator-definition.
  4557.  
  4558.  
  4559. Clarifications
  4560.  
  4561.  
  4562. The ARM left a lot unsaid. The committee has been trying to fill in the
  4563. details. Here's the summary of most of the issues clarified so far:
  4564. The name of an untagged class named in a typedef is the class name for linkage
  4565. purposes only.
  4566. A function can be declared pure virtual in a derived class even if it's
  4567. already defined in a base class.
  4568. A destructor can be declared pure virtual, but it must also be defined.
  4569. new T[0] returns a unique pointer, thus implying that the allocated array may
  4570. consume memory.
  4571. A pointer to a T member of a class cannot point to a T & member.
  4572. Member bitfields cannot be declared static.
  4573. A class may declare a static data member with an incomplete type.
  4574. Lookup for names in an out-of-line member function looks in base classes
  4575. before looking in enclosing classes.
  4576. An aggregate initializer can only initialize an aggregate's non-static data
  4577. members.
  4578. Constructors and destructors for volatile objects may be compiled with or
  4579. without volatile semantics.
  4580. A C++ translator must look up the T in x. T::m (or p->T::m) as a type in two
  4581. contexts -- (1) in the class scope of x (or *p), and (2) in the context of x
  4582. -- but find T in only one.
  4583. For the purpose of type lookup, the class name T is also considered a nested
  4584. class member of class T.
  4585. cv-qualifiers are not removed from function return types.
  4586. cv-qualifiers that appear in the type-modifier for an array of T are applied
  4587. to the type T and not to the array type itself.
  4588. Pointers to members with reference or (possibly cv-qualified) void type are
  4589. prohibited.
  4590. Redundant cv-qualifiers within a single declaration are prohibited.
  4591. References to (possibly cv-qualified) void are prohibited
  4592. Function parameters that include references to incomplete array types are
  4593. prohibited.
  4594. Overload resolution does not consider functions as candidates if they cannot
  4595. be called due to invalid reference initialization.
  4596. cv-qualifiers are significant in overload resolution.
  4597. Conversion function names may employ multiple pointers.
  4598. Expression evaluation only considers overloaded operators when at least one
  4599. operand has user defined type.
  4600. cv-qualified reference types (e.g., T &const n) are prohibited.
  4601. Static objects defined at file scope may be initialized at translation time
  4602. (non-dynamically).
  4603. As you can see, we at CUJ have plenty to write about for years to come.
  4604.  
  4605.  
  4606. Meeting Dates, Etc.
  4607.  
  4608.  
  4609. WG21+X3J16 will meet three times in 1995:
  4610. March 5-10 in Austin, TX USA, hosted by Motorola
  4611. July 9-14 in the San Francisco Bay area, CA USA, hosted by Sun Microsystems
  4612. November 5-10 in Tokyo, Japan, hosted by ITSCJ
  4613. If you would like to participate in the standards process as a member of
  4614. X3J16, contact the vice-chair:
  4615. Jose'e Lajoie
  4616. IBM Canada Laboratory
  4617. 844 Don Mills Rd.
  4618. North York, Ontario M3C
  4619. 1V7 Canada
  4620. (416)448-2734
  4621. josee@vnet.ibm.com
  4622. References
  4623.  
  4624. [1] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference
  4625. Manual (Addison-Wesley, 1990).
  4626. [2] P.J. Plauger. The Draft Standard C++ Library (Prentice-Hall, 1995).
  4627. [3] Bjarne Stroustrup. The C++ Programming Language, 2nd. ed. (Addison-Wesley,
  4628. 1991).
  4629. [4] Bjarne Stroustrup. The Design and Evolution of C++ (Addison-Wesley, 1994).
  4630. Table 1 Standard Organizations Affecting C and C++
  4631.  US Standards
  4632. ----------------------------------------------------------------
  4633. ANSI the American National Standards Institute
  4634. X3 the ANSI committee for Information Processing
  4635.  Systems
  4636. X3J11 the X3 technical committee standardizing C
  4637. X3J16 the X3 technical committee standardizing C++
  4638.  
  4639.  International Standards
  4640. ----------------------------------------------------------------
  4641. ISO The International Organization for Standardization, an
  4642.  organization of national standards bodies such as
  4643.  ANSI (USA), AFNOR (France), BSI (UK), DIN
  4644.  (Germany), JTSC (Japan), SCC (Canada) and many
  4645.  others
  4646. IEC the International Electrotechnical Commission
  4647. JTC1 the Joint Technical Committee (of ISO and IEC) on
  4648.  Information Technology
  4649.  
  4650. SC22 the JTC1 Sub-Committee for programming languages
  4651. WG14 the SC22 Working Group standardizing C
  4652. WG21 the SC22 Working Group standardizing C++
  4653. Table 2 C/C++ Standards Quick Reference Guide
  4654. and (+) false (+) signed
  4655. and_eq (+) float sizeof
  4656. asm for static
  4657. auto friend static_cast (+)
  4658. bitand (+) goto struct
  4659. bitor (+) if switch
  4660. bool (+) inline template
  4661. break int this
  4662. case long throw (+)
  4663. catch (+) mutable (+) true (+)
  4664. char namespace (+) try (+)
  4665. class new typedef
  4666. compl (+) not (+) typeid (+)
  4667. const not_eq (+) typename (+)
  4668. const_cast (+) operator union
  4669. continue or (+) unsigned
  4670. default or_eq (+) using (+)
  4671. delete private virtual
  4672. do protected void
  4673. double public volatile
  4674. dynamic_cast (+) register wchar_t (+)
  4675. else reinterpret_cast (+) while
  4676. enum return xor (+)
  4677. explicit (+) short xor_eq (+)
  4678. extern
  4679. (+) indicates a new keyword (not found in the ARM)
  4680.  
  4681.  
  4682.  
  4683.  
  4684.  
  4685.  
  4686.  
  4687.  
  4688.  
  4689.  
  4690.  
  4691.  
  4692.  
  4693.  
  4694.  
  4695.  
  4696.  
  4697.  
  4698.  
  4699.  
  4700.  
  4701.  
  4702.  
  4703.  
  4704.  
  4705.  
  4706.  
  4707.  
  4708.  
  4709.  
  4710.  
  4711.  
  4712.  
  4713.  
  4714.  
  4715.  
  4716.  
  4717.  
  4718.  
  4719.  
  4720.  
  4721.  
  4722.  
  4723.  
  4724.  
  4725.  
  4726.  
  4727.  
  4728.  
  4729.  
  4730.  
  4731.  
  4732.  
  4733.  
  4734.  
  4735.  
  4736.  
  4737.  
  4738.  
  4739.  
  4740.  
  4741.  
  4742.  
  4743.  
  4744.  
  4745.  
  4746.  
  4747.  
  4748.  
  4749. Questions & Answers
  4750.  
  4751.  
  4752. Are Marching Pointers Really Faster?
  4753.  
  4754.  
  4755.  
  4756.  
  4757. Kenneth Pugh
  4758.  
  4759.  
  4760. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++
  4761. language courses for corporations. He is the author of All On C, C for COBOL
  4762. Programmers, and UNIX for MS-DOS Users, and was a member of the ANSI C
  4763. committee. He also does custom C/C++ programming and provides
  4764. SystemArchitectonicssm services. His address is 4201 University Dr., Suite
  4765. 102, Durham, NC 27707. You may fax questions for Ken to (919) 489-5239, Ken
  4766. also receives email at kpugh@allen.com (Internet) and on Compuserve
  4767. 70125,1142.
  4768.  
  4769.  
  4770. Q
  4771. I always enjoy your column because it is always relevant. My work is
  4772. scientific computing. In your piece "Increasing Execution Speed" you contrast
  4773. zeroing an array using an explicit index with a method that uses pointer
  4774. incrementing. You comment that the pointer increment is "usually faster." Oh?
  4775. My work chart looks like Table 1.
  4776. I coded up a comparison of a million zeros each way (Listing 1). On my 486
  4777. with Turbo C they come out nearly the same: 1.2 seconds for the index versus
  4778. 1.0 seconds for the pointer. My friend Walter Herrick tested the programs on a
  4779. couple of other platforms and got nearly identical results (Listing 2), Why so
  4780. nearly equal? Microprocessors do integer multiplications in hardware, which is
  4781. fast, compared with shipping bytes to memory.
  4782. There are two good reasons to avoid addressing an array by a moving pointer,
  4783. using an index instead. First, the pointer method does not generalize to
  4784. arrays of more than one dimension. Second, the marching pointer is not
  4785. language-portable. In scientific programming, one frequently has to move
  4786. algorithms between C, FORTRAN, and Pascal. Only C allows moving pointers.
  4787. Other languages, especially those with built-in array index validation, do not
  4788. permit pointers to move.
  4789. It is true that the K&R book has many examples of marching pointers. But that
  4790. does not make marching pointers a good idea! K&R was written when multiplies
  4791. were expensive and adds were cheap. We are beyond that now, and can afford a
  4792. different point of view, in which clarity is regarded as being more important
  4793. than minimizing source code length. For one view of how to restrict ANSI C as
  4794. to promote clean coding (no side effects, one consequence per line of code,
  4795. transportable constructs, etc.) see the article by Helene Ballay and Rainer
  4796. Store, "A Tool for Checking C Coding Conventions," CUJ, July, 1994, p. 41.
  4797. From the sidebar I judge that PJP disagrees with those authors but in my
  4798. experience (moving code), I support their view.
  4799. Mike Lampton
  4800. Berkeley, CA
  4801. A
  4802. Thanks for sharing your test results with us. It's true that in many cases,
  4803. with today's increased microprocessor speeds, the relative efficiency of a
  4804. particular approach is unimportant. This is not necessarily the case for
  4805. specialized applications, such as in embedded systems, which may use less
  4806. powerful chips. The use of instruction caches and memory caches complicates
  4807. the issue by making it difficult to calculate which approach will always be
  4808. most efficient.
  4809. With GUI interfaces becoming much more common these days, much of the CPU time
  4810. is spent in interacting with the user. The application programmer usually does
  4811. not have much opportunity to make the GUI efficient (other than to insure that
  4812. any repainting of the screen is minimal -- i.e. the entire virtual screen is
  4813. not redrawn). I estimate that about 80% of the CPU time in a typical
  4814. application is spent in GUI libraries and code. The reminder is in "real"
  4815. processing. If you were able to double the efficiency of the real processing,
  4816. you would only cut 10% from the overall CPU time.
  4817.  
  4818.  
  4819. Memory overwrites
  4820.  
  4821.  
  4822. Q
  4823. I'm a longtime subscriber to The C Users Journal and I've written a program
  4824. using Turbo C v3.0 that writes to memory that doesn't belong to it,
  4825. specifically memory that MS-DOS uses. After running the program, in most cases
  4826. memory is so corrupted that DOS doesn't recognize the keyboard, COMMAND.COM
  4827. fails to reload, or, worse yet, DOS's information on the hard disk format has
  4828. been overwritten and thereafter any write to the disk destroys data there.
  4829. I've done the usual in trying to debug the program, including using MemCheck
  4830. v3.0 and the latest PC-Lint. I have not figured out how to zero in on the code
  4831. that is responsible using either of these two commercial tools.
  4832. I suspect some third-party commercial graphics libraries that I am using are
  4833. doing the actual writing to DOS memory, but haven't been able to pinpoint the
  4834. problem.
  4835. The next step I want to try is to generate checksums for various blocks of
  4836. memory that DOS uses and then throughout my program periodically recalculate
  4837. the checksums in an effort to move in closer to the bad code. The problem
  4838. here, however, is that I've been unable to locate any detailed maps of memory
  4839. that DOS uses. No one claims to know where I can obtain them.
  4840. Do you know of any source for DOS memory maps or do you have any suggestions
  4841. on how I can find the cause of my problem?
  4842. Dennis C. Fait
  4843. Butler, PA
  4844. A
  4845. Memory problems are the bane of the C programmer. Give a programmer an address
  4846. and there is no telling what he or she may do with it. PC-Lint can indicate
  4847. potential problems in source code for many types of pointer errors. However it
  4848. cannot analyze dynamic errors. For example, it is easy (too easy some might
  4849. say) to increment an address beyond its proper bounds by looping too many
  4850. times. Debug output placed in the source code can pinpoint these types of
  4851. errors. Since it sounds like you do not have access to the source code for the
  4852. third party libraries, you will have to try a more indirect approach.
  4853. Some of your problems may be caused by errors other than memory overwrites.
  4854. For example, I know that several DOS versions back you could cause disk
  4855. corruption by re-opening the same file for writing without closing it. (I have
  4856. not tried this recently, as my disks have grown too large for easy repair in
  4857. case it remains a problem with MS-DOS.)
  4858. A good book on MS-DOS programming will typically include some sort of MS-DOS
  4859. memory map, though maybe not to the level of detail you are looking for. You
  4860. might check out Dos Internals, by Geoff Chappell [1]. This book dedicates a
  4861. couple of chapters to MS-DOS memory management; it may shed some light on your
  4862. problem. Alternatively, run the MS-DOS MEM command with the /D option. This
  4863. will give you a fairly detailed snapshot of your memory configuration. Most
  4864. likely you will discover that MS-DOS is not confined to one contiguous region,
  4865. which can make this particular debugging approach difficult.
  4866. Fortunately, just as there are many ways to create memory overwrite errors,
  4867. there are many possible ways to track down those errors. For example, you can
  4868. temporarily bypass calls to the suspected routines. You ought to use a dynamic
  4869. test to do this, rather than using conditional compilation. Use something like
  4870. this:
  4871. if (full_program)
  4872. potential_offending_function();
  4873. as opposed to this:
  4874. #ifdef FULL_PROGRAM
  4875. potential_offending_function();
  4876. #endif
  4877. Using a dynamic test will keep the memory layout of your executable close to
  4878. that for which you are having problems.
  4879. You could also try using different versions and configurations of MS-DOS and
  4880. see if the results are any different. The memory layout would be different and
  4881. so you should get different results. If no differences occur, the error may be
  4882. in overwritting buffers internal to your program rather than in MS-DOS.
  4883. Another approach would be to compile it in protected mode and link it with a
  4884. protected mode linker. Your graphic libraries may not necessarily work in that
  4885. mode, although many of them do. Memory access errors in protected mode will
  4886. cause a fault, rather than crash MS-DOS.
  4887. The above is not an exhaustive list. If nothing else works, you can, of
  4888. course, try another graphics library. I admit that's not a very desirable
  4889. option. Hopefully its interface will be similar to the one you are currently
  4890. using, so you won't have to re-write much code.
  4891. Reference
  4892. [1] Geoff Chappell. Dos Internals (Addison-Wesley, 1994), pp. 131-192.
  4893. Table 1 Cost of zeroing an array: indexing vs. pointer operatons
  4894. Index Pointer Cost
  4895. ------------------------------------------------------
  4896.  
  4897. increment index decrement count equal
  4898. get address get address add 8*i versus add 8
  4899. place the zero place the zero equal
  4900.  
  4901. Listing 1 A program to compare indexing vs. pointer addressing for speed
  4902. /* aspeed.c array vs pointer speed test */
  4903. #include <stdio.h>
  4904. #include <time.h> /* gettime() */
  4905.  
  4906. #define SIZE 1000
  4907. #define REPS 1000
  4908.  
  4909. void zerobyincrement(double array[], int howmany);
  4910. void zerobydecrement(double array[], int howmany);
  4911. void zerobypointer(double array[], int howmany);
  4912. double fetchtime( void);
  4913.  
  4914. void main()
  4915. {
  4916. double darray[SIZE];
  4917. int i;
  4918. double t0, t1;
  4919. printf("incrementing index...\n");
  4920. t0 = fetchtime();
  4921. for (i=0; i<REPS; i++)
  4922. zerobyincrement(darray, SIZE);
  4923. t1 = fetchtime();
  4924. printf("time... %9.21f \n", (t1-t0) / CLOCKS_PER_SEC);
  4925. printf("decrementing index...\n");
  4926. t0 = fetchtime();
  4927. for (i=0; i<REPS; t++)
  4928. zerobydecrement(darray, SIZE);
  4929. t1 = fetchtime();
  4930. printf("time... %9.21f \n", (t1-t0) / CLOCKS_PER_SEC);
  4931. printf("marching pointer... \n");
  4932. t0 = fetchtime();
  4933. for (i=0; i<REPS; i++)
  4934. zerobypointer(darray, SIZE);
  4935. t1 = fetchtime();
  4936. printf("time... %9.21f \n", (t1-t0) / CLOCKS_PER_SEC);
  4937. }
  4938.  
  4939. void zerobyincrement(double array[], int howmany)
  4940. {
  4941. int i;
  4942. for (i=0; i<howmany; i++)
  4943. array[i] = 0.0;
  4944. }
  4945.  
  4946. void zerobydecrement(double array[], int howmany)
  4947. {
  4948. int i;
  4949. for (i=howmany-1; i>=0; i--)
  4950. array[i] = 0.0;
  4951. }
  4952.  
  4953. void zerobypointer(double array[], int howmany)
  4954. {
  4955. while (howmany--)
  4956.  
  4957. *array++ = 0.0;
  4958. }
  4959.  
  4960. double fetchtime( void )
  4961. {
  4962. return (double) clock();
  4963. }
  4964. /* End of File */
  4965.  
  4966.  
  4967. Listing 2 Results of running program aspeed.c (Listing 1)
  4968. RESULTS:
  4969.  
  4970. increment decrement pointer
  4971. 286 6MHz, 1 million zeros: 50.53us 51.36us 54.81us
  4972. 486 50MHz, l0 million zeros: 1.21us 1.16us 1.08us
  4973.  
  4974. 486-50 using Microsoft C 6.0 increment decrement pointer
  4975. 1.04 1.04 0.93
  4976. Sun SPARCserver 1000 using Sun C compiler fully optimized
  4977. 0.06 0.08 0.06
  4978.  
  4979.  
  4980.  
  4981.  
  4982.  
  4983.  
  4984.  
  4985.  
  4986.  
  4987.  
  4988.  
  4989.  
  4990.  
  4991.  
  4992.  
  4993.  
  4994.  
  4995.  
  4996.  
  4997.  
  4998.  
  4999.  
  5000.  
  5001.  
  5002.  
  5003.  
  5004.  
  5005.  
  5006.  
  5007.  
  5008.  
  5009.  
  5010.  
  5011.  
  5012.  
  5013.  
  5014.  
  5015.  
  5016.  
  5017.  
  5018.  
  5019.  
  5020. CUG New Releases
  5021.  
  5022.  
  5023. MICRO-C, MIMEQP, BSPline, and More
  5024.  
  5025.  
  5026.  
  5027.  
  5028. Victor R. Volkman
  5029.  
  5030.  
  5031. Victor R. Volkman received a BS in Computer Science from Michigan
  5032. Technological University. He has been a frequent contributor to C/C++ Users
  5033. Journal since 1987. He is currently employed as Senior Analyst at H.C.I.A. of
  5034. Ann Arbor, Michigan. He can be reached by dial-in at the HAL 9000 BBS (313)
  5035. 663-4173 or by Usenet mail to sysop@hal9k.com.
  5036.  
  5037.  
  5038.  
  5039.  
  5040. New Acquisitions
  5041.  
  5042.  
  5043. MICRO-C (CUG #422): MICRO-C C compiler for the PC, over 70 example programs
  5044. for use with MICRO-C, and demonstration compiler for embedded systems with
  5045. simulators.
  5046. RECIO, MIMEQP, ACCTPOST, RDCF, and BSPLINE (CUG #423): a bevy of tools from
  5047. authors from around the U.S., including:
  5048. Stream-style record I/O
  5049. MIME binary encode/decode for email
  5050. General ledger posting w/32-bit arithmetic library for MS-DOS
  5051. Re-entrant DOS-Compatible File System for embedded systems
  5052. Classic BSPLINE rendering algorithm
  5053.  
  5054.  
  5055. CUG #422: MICRO-C C Compiler
  5056.  
  5057.  
  5058. Dave Dunfield (Nepean, Ontario, Canada) submits an entire suite of tools from
  5059. the MICRO-C C compiler development system. This submission includes the
  5060. MICRO-C C compiler itself (for MS-DOS), more than 70 useful sample programs
  5061. with full C source, and a demonstration version of MICRO-C for embedded
  5062. systems. MICRO-C is a tiny compiler which can run in less than 32Kb of RAM,
  5063. yet is highly independent of CPU and OS. Other distributions of this compiler
  5064. will run on 68HC08, 6809, 68HC11, 68HC16, 8051/52, 8080/8085, and 8096 CPUs.
  5065. The CUG Library distribution includes a fully functional MICRO-C compiler
  5066. executable, built for the MS-DOS 80x86 environment. This version generates
  5067. code in .ASM format, so Microsoft MASM, Borland TASM, or equivalent are
  5068. required (not included). MICRO-C version 3.02 (as released on 03/22/94) is
  5069. immediately available as CUG#422 in a set of four diskettes.
  5070. MICRO-C provides much more functionality than Small-C and its many
  5071. derivatives. Specifically, MICRO-C supports all C statements, operators, and
  5072. preprocessor directives as well as inline assembly code. MICRO-C includes data
  5073. types for int, char, unsigned, struct, union, pointers, and typecasting. In
  5074. other words, MICRO-C gives you everything possible except for typedef, long,
  5075. double, float, enum, and bit fields. The runtime library does include a long
  5076. arithmetic package with arbitrary precision up to 256 bits.
  5077. Even if you're not especially interested in the MICRO-C compiler, the you may
  5078. wish to take advantage of the collection of more than 70 sample programs.
  5079. Although there are simply too many to catalog here, I've listed two dozen of
  5080. the most interesting:
  5081. CCREF -- C source cross referencing program
  5082. COMEXT -- Extract comments from C sources
  5083. OBSCURE -- Make C program unreadable (but it still
  5084.  compiles)
  5085. PCC -- Pretty Printer for C (source formatter
  5086. CALC -- A TSR programmers (HEX/DECIMAL
  5087.  calculator
  5088. CMOS -- Read/Write/Verify CMOS RAM from/to/with
  5089.  disk file
  5090. CSET -- TSR map of IBM PC character set
  5091. DIFF -- Displays differences between text files
  5092. GREP -- Like UNIX "GREP" search utility
  5093. HEM -- Hardware Exception Monitor TSR to trap
  5094.  unexpected interrupts
  5095. LZC -- Laser commander TSR to control
  5096.  HP-compatible printers
  5097. MEMSAVE -- Saves memory image to file
  5098. MTERM -- Tiny (10K!) TSR ANSI terminal with
  5099.  XMODEM
  5100. SHOWEXE -- Displays information about an .EXE file
  5101. TFB -- TSR File Browser
  5102. VALIDATE -- PD version of McAfee's validate. Verify file
  5103.  with two CRCs
  5104.  
  5105. LAPTALK -- A terminal program with script interpreter
  5106. XMODEM -- External file transfer program
  5107. MICROCAD -- Mouse-based drawing program
  5108. FE -- Font Editor
  5109. ASM86 -- 8086 assembler
  5110. BASIC -- A simple BASIC interpreter
  5111. DIS85 -- 8085 Cross Disassembler
  5112. TTT3D -- 3 dimensional tic-tac-toe
  5113. All files in the MICRO-C archives are for personal use only. Any commercial
  5114. use of information or programs contained in these archives requires written
  5115. permission from Dunfield Development Systems. You can obtain source code for
  5116. the MICRO-C compiler by purchasing a license for $100 from Dunfield
  5117. Development Systems.
  5118.  
  5119.  
  5120. CUG #423: RECIO, MIMEQP, ACCTPOST, RDCF, and BSPLINE
  5121.  
  5122.  
  5123. The CUG Library has always accommodated C/C++ archives both big and small.
  5124. This month, I've compiled an anthology of five small but outstanding source
  5125. archives. William Pierpoint (Camarillo, CA) submits his comprehensive library
  5126. for streamstyle record I/O. Karl Hahn (Sarasota, FL) contributes MIME binary
  5127. encode/decode routines for use with e-mail tools. Philip Erdelsky (San Diego,
  5128. CA) releases source for general ledger posting with 32-bit math library, and a
  5129. reentrant, DOS-Compatible File System for embedded systems. Last, Keith
  5130. Vertanen (Pine Springs, MN) sends his brief but succinct implementation of the
  5131. BSPLINE rendering algorithm. Again, all five archives are immediately
  5132. available on a single diskette as CUG volume #423.
  5133.  
  5134.  
  5135. CUG #423A: RECIO -- Record Input Made Easy
  5136.  
  5137.  
  5138. The RECIO library contains more than 50 functions and macros enabling file
  5139. input in which each line becomes a data record, with each record subdivided
  5140. into fields. Fields may be either character delimited or column delimited.
  5141. RECIO's learning curve is not steep, since many functions are based on
  5142. analogous counterparts in stdio. RECIO is freeware and is protected by the GNU
  5143. Public License. Version 2.00 (as released 04/16/94) appears along with several
  5144. unrelated archives on CUG volume #423.
  5145. Since virtually every program has to do input or output, C programmers are
  5146. very familiar with the stdio library. Many functions in the recio library are
  5147. analogous to those in the stdio library, as shown by the following table:
  5148. Analogous stdio/recio components
  5149.  stdio recio
  5150. ----------------------------
  5151. FILE REC
  5152. FOPEN_MAX ROPEN_MAX
  5153. stdin recin
  5154. fopen ropen
  5155. fclose rclose
  5156. fgets rgetrec
  5157. fscanf rgeti, rgetd, rgets, ...
  5158. clearerr rclearerr
  5159. feof reof
  5160. ferror rerror
  5161. RECIO includes a makefile only for Borland Turbo C, although it should work
  5162. with other platforms as well.
  5163.  
  5164.  
  5165. CUG #423B: MIMEQP -- A Better Encode/Decode For E-mail
  5166.  
  5167.  
  5168. MIMEQP inputs files that are mostly ASCII, but contain some non-ASCII
  5169. characters, and encodes them so that the output file is all ASCII. The
  5170. characters that were ASCII remain so, so the encoded file is human-readable.
  5171. The encoding algorithm also limits line lengths to 72 characters. MIMEQP is
  5172. useful for sending files containing non-ASCII characters through mail servers.
  5173. MIMEQP (or MIME Quoted-Printable) is defined in RFC 1341. MIMEQP (as released
  5174. on 05/22/93) appears along with several unrelated archives on CUG volume #423.
  5175. MIME is an acronym for Multipurpose Internet Mail Extensions. It builds on the
  5176. older standard by defining additional fields for mail message headers. These
  5177. headers describe new types of content and organization for messages.
  5178. MIME allows mail messages to contain the following:
  5179. Multiple objects in a single message
  5180. Text of unlimited line length or overall length
  5181. Character sets other than ASCII
  5182. Multi-font messages
  5183. Binary or application-specific files
  5184. Images; Audio, Video, and multi-media messages
  5185. MIMEQP encoding replaces all control characters except '\n' and '\t,' all
  5186. occurrences of '=', and all characters whose ASCII code is greater than 127
  5187. with =XX, where XX is the hex value of the ASCII code. The CUG library
  5188. distribution includes a substantial overview of the MIME standard by Mark
  5189. Grand, along with an MS-DOS executable.
  5190.  
  5191.  
  5192. CUG #423C: ACCTPOST General Ledger Utility
  5193.  
  5194.  
  5195. The Plain Vanilla Posting Program II is a simple program that takes over three
  5196. tedious accounting tasks: posting from the general journal to the general
  5197. ledger, drawing a trial balance, and putting account balances into a report.
  5198. The Plain Vanilla Posting Program II is written for the compact memory model
  5199. (near function, far data) in Turbo C 2.0 for MS-DOS. The author claims some
  5200. source portability to UNIX variants as well. The Plain Vanilla Posting Program
  5201. II (as released on 09/01/90) is included with unrelated source archives on CUG
  5202. volume #423.
  5203. The only non-portable part of the program appears in ARITHMET.C, which
  5204. contains embedded assembly language code. This allows ACCTPOST to catch
  5205. arithmetic overflows, which would be disastrous for an accounting application
  5206. if undetected. Otherwise, ACCTPOST is just a simple 32-bit arithmetic package,
  5207. with only five operations: addition, negation, multiplication, division by 10,
  5208. and remainder computation for a 32-bit number divided by 10. The last two
  5209. operations cannot produce overflows and could have written in standard C.
  5210. Porting to a machine with native 32-bit arithmetic should be simple.
  5211. Although the Plain Vanilla Posting Program II is copyrighted, it carries only
  5212. one restriction: you may not sell the program or any program derived from it.
  5213. You may give it away or charge a reasonable fee for media duplication, but may
  5214. not charge anything for the software itself.
  5215.  
  5216.  
  5217.  
  5218. CUG #423D: Re-entrant DOS-compatible File System
  5219.  
  5220.  
  5221. RDCF is a reentrant and ROMable DOS-compatible file system, designed for use
  5222. with floppy diskettes and hard disk partitions that do not exceed 32 MB in
  5223. size. This distribution also includes a simple disk-caching package designed
  5224. for use with RDCF, which may also be used separately. The distribution
  5225. includes an MS-DOS utility called FILES, written primarily to test RDCF, in
  5226. both source and executable form. FILES performs a number of operations on DOS
  5227. files, including some that cannot be performed from the DOS command line. For
  5228. example, FILES' DIR command shows the remains of deleted files. This
  5229. distribution includes complete C source code and documentation for FILES,
  5230. including a fairly detailed description of the DOS file system.
  5231. RDCF is copyrighted, but it is freeware. You may copy it and pass it on
  5232. freely, as long as you pass on the entire, unmodified package, with its
  5233. copyright notices intact. You may not resell it, although you may charge a
  5234. reasonable fee for diskette duplication, shipping, and handling.
  5235.  
  5236.  
  5237. CUG #423E: Classic BSPLINE Rendering Algorithm
  5238.  
  5239.  
  5240. A spline is a mathematical construct from numerical analysis which is used to
  5241. fit a curve to an arbitrary set of points. It has obvious uses in statistics
  5242. and computer graphics rendering. For a description of spline theory and
  5243. algorithm implementation, see Elementary Numerical Analysis by Kendall
  5244. Atkinson (1985, Wiley & Sons). BSPLINE performs polynomial interpolation and
  5245. returns an array of coefficents to describe a spline. BSPLINE was written in
  5246. C++ for the Borland compiler, though it uses few, if any, features of C++. The
  5247. library uses function calls from the Borland Graphics Interface to render the
  5248. bspline on EGA or VGA displays. BSPLINE (as released on 03/11/94) is included
  5249. with other unrelated archives on CUG volume #423.
  5250.  
  5251.  
  5252.  
  5253.  
  5254.  
  5255.  
  5256.  
  5257.  
  5258.  
  5259.  
  5260.  
  5261.  
  5262.  
  5263.  
  5264.  
  5265.  
  5266.  
  5267.  
  5268.  
  5269.  
  5270.  
  5271.  
  5272.  
  5273.  
  5274.  
  5275.  
  5276.  
  5277.  
  5278.  
  5279.  
  5280.  
  5281.  
  5282.  
  5283.  
  5284.  
  5285.  
  5286.  
  5287.  
  5288.  
  5289.  
  5290.  
  5291.  
  5292.  
  5293.  
  5294.  
  5295.  
  5296.  
  5297.  
  5298.  
  5299.  
  5300. Editor's Forum
  5301. I just got to know two communities better. I've been a member of both for lo
  5302. these many years, but not the active participant I probably should have been.
  5303. One community is the town I live in. The other is the global network of people
  5304. I interact with professionally. Both suddenly loomed larger in my personal
  5305. sphere.
  5306. The town of Concord, Massachusetts still holds annual town meetings to conduct
  5307. its business. This is genuine participatory democracy at its finest -- you
  5308. literally must attend and stand up to be counted to vote on an issue. The bad
  5309. news is that typically only a few percent of the town actually attends town
  5310. meetings. I, for one, have in the past left this duty to my fellow townsfolk,
  5311. who seem to be doing just fine at it, thank you.
  5312. But a group of people decided to oppose a recent town vote to add six units of
  5313. affordable housing to our rather wealthy backwater. Yes, I said six units.
  5314. They raised the signatures needed to call a special town meeting to rescind
  5315. the vote. I was pleased to see 1,400 of my fellow citizens turn out to soundly
  5316. defeat the move, more than doubling the record for the largest town meeting in
  5317. the past. It's enough to renew my faith in participatory democracy.
  5318. Within hours of that event, I logged onto the internet with a Mosaic browser
  5319. for the first time. Before I knew it, I had skimmed around the world several
  5320. times, noting places and things undreamed of in my earlier philosophy. My
  5321. hotlist is growing nightly as I chase strands of the World Wide Web. I've yet
  5322. to tire of the adventure. E-mail is nothing like this.
  5323. I can hardly wait for a dedicated high-speed link. Already, I've set aside a
  5324. machine to serve as my "store front" on the Web, and I'm planning my home page
  5325. with all the alacrity of a pre-schooler at the easel with finger-paints. It's
  5326. enough to renew my enthusiasm for the overworked term "global village."
  5327. In a world with decaying neighborhoods, cynical politics, and weakening
  5328. industries, it's a pleasure to find the seeds of new communities and
  5329. enterprises. They won't always look like the ones we left behind, or the ones
  5330. we may dream of, but they are new and exciting for all that. It reminds me of
  5331. a bit of hillbilly wisdom from my roots in West Virginia -- We ain't what we
  5332. wanna be, and we ain't what we're gonna be, but we ain't what we wuz.
  5333. Happy new year.
  5334. P.J. Plauger
  5335. pjp@plauger.com
  5336.  
  5337.  
  5338.  
  5339.  
  5340.  
  5341.  
  5342.  
  5343.  
  5344.  
  5345.  
  5346.  
  5347.  
  5348.  
  5349.  
  5350.  
  5351.  
  5352.  
  5353.  
  5354.  
  5355.  
  5356.  
  5357.  
  5358.  
  5359.  
  5360.  
  5361.  
  5362.  
  5363.  
  5364.  
  5365.  
  5366.  
  5367.  
  5368.  
  5369.  
  5370.  
  5371.  
  5372.  
  5373.  
  5374.  
  5375.  
  5376.  
  5377.  
  5378.  
  5379.  
  5380.  
  5381.  
  5382.  
  5383.  
  5384.  
  5385.  
  5386.  
  5387. New Products
  5388.  
  5389.  
  5390. Industry-Related News & Announcements
  5391.  
  5392.  
  5393.  
  5394.  
  5395. Pure Software Ships Purify 3 and PureCoverage 1.0
  5396.  
  5397.  
  5398. Pure Software Inc. has begun shipping Purify 3.0 and PureCoverage 1.0. Purify
  5399. 3.0 is a software quality development tool that detects run-time errors and
  5400. memory leaks in C/C++ applications. Purify incorporates Pure Software OCI
  5401. technology. OCI examines the object code and inserts checking instructions
  5402. around the memory functions to monitor usage at run time and report illegal
  5403. memory accesses and leaks. OCI lets Purify analyze applications including
  5404. shared and third-party libraries.
  5405. Features of Purify 3.0 include a GUI that provides message browsing while an
  5406. application is running and an outline view which displays error messages and
  5407. simplifies navigation. Also included in Purify 3.0 is single-click access to
  5408. source code, which lets developers make changes at the point where an error
  5409. occurred.
  5410. Purify 3.0 is integrated with PureCoverage 1.0, Pure Software's code coverage
  5411. analysis product. PureCoverage 1.0 lets users manage, manipulate, and analyze
  5412. data into a variety of formats and reports. Data can be viewed through Purify
  5413. 3.0's outline browser and then manipulated according to user specifications.
  5414. Inserted at the object level, PureCoverage integrates into the development
  5415. environment. PureCoverage supports C/C++ and FORTRAN, and runs on multiple
  5416. platforms including Sun, Solaris, and HP.
  5417. Purify 3.0 is priced at $1,298. PureCoverage 1.0 is priced at $898. For more
  5418. information contact Pure Software Inc., 1309 S. Mary Ave., Sunnyvale, CA
  5419. 94087, (408) 720-1600; FAX: (408) 720-9200; E-mail: info@pure.com.
  5420.  
  5421.  
  5422. ARSoftware Releases ARC++ 2.0
  5423.  
  5424.  
  5425. ARSoftware has released ARC++ v2.0, a C/C++ development tool. According to the
  5426. company, with a few adjustments to the existing make or project files, ARC++
  5427. automatically determines whether a header file change affects previously
  5428. compiled modules and alters the make process accordingly, giving the user
  5429. control over recompiles.
  5430. Features of ARC++ v2.0 include exportable classes that eliminate the need for
  5431. header files and intelligent macros that provide encapsulated code generation.
  5432. ARC++'s macro capability automatically analyzes class definitions, which can
  5433. loop, branch, and access the parse tree/symbol table to generate code based on
  5434. previous declarations. Defined within C++'s block structure, these macros are
  5435. capable of being inherited. Callback macros in multiple base classes ensure
  5436. that derived calls have the appropriate support code generated. Other features
  5437. of ARC++ include: overloaded enumerators, bound function pointers, prototyped
  5438. Vararg functions, automatic functions, arrays with bounds, modify detection,
  5439. user-defined operations, combination operators, user-defined modifiers, and
  5440. hidden arguments.
  5441. ARC++ v2.0 for DOS and Macintosh is priced at $249, and $299 for UNIX. Upgrade
  5442. pricing for existing users is $29 for DOS and Macintosh, and $39 for UNIX. For
  5443. more information contact ARSoftware, 8201 Corporate Dr., Suite 1110, Landover,
  5444. MD 20785, (800) 257-0073 or (301) 459-3773; On-line catalog:
  5445. URL:http://arsoftware.arclch.com.
  5446.  
  5447.  
  5448. Cygnus Announces GUI for GNU Debugger
  5449.  
  5450.  
  5451. Cygnus Support has announced plans to release a GUI for their GNU Debugger,
  5452. GDB. The GUI for GDB provides programmers with a consistent interface for
  5453. native UNIX development, as well as cross-platform development hosted on UNIX
  5454. X11 and MS-Windows 3.1 platforms. The Cygnus GUI allows users to extend or
  5455. modify the GUI based on their user-interface preferences.
  5456. According to Cygnus, the design of the GDB interfaces emphasizes the
  5457. information display and the direct manipulation of displayed objects.
  5458. Additional features of the GUI include windows for source, object code, data
  5459. display, stack frames, breakpoint/watchpoint lists, and a target state window.
  5460. The GUI also includes context-sensitive, on-line help and keyboard shortcuts
  5461. for common operations such as single-stepping. Both high-level and low-level
  5462. graphics capabilities will also be available in the GUI. All features are
  5463. available to both native and cross-debugging configurations of GDB. The GUI
  5464. will be available on all host/target platforms currently supported by Cygnus.
  5465. Source code is also available.
  5466. The company plans to release the UNIX version of the GUI for GDB January 1,
  5467. 1995, and the Windows version April 2, 1995. For more information contact
  5468. Cygnus Support, 1937 Landings Dr., Mountain View, CA 94043, (415) 903-1400;
  5469. FAX: (415) 903-0122.
  5470.  
  5471.  
  5472. Apogee Announces Apogee-C/C++ and Apogee-FORTRAN 77/90 Compilers
  5473.  
  5474.  
  5475. Apogee Software has announced Apogee-C/C++ and Apogee-FORTRAN 77/90 compilers
  5476. for SPARC. Apogee-C/C++ and Apogee-FORTRAN 77/90 compile programs written in
  5477. C/C++ and FORTRAN 77 or FORTRAN 90 into machine code optimized for RISC-based
  5478. computers.
  5479. Apogee-C/C++ is an optimizing compiler accepting C/C++. The C dialects
  5480. accepted include the ISO/ANSI C standard and Kernighan & Richie C. The C++
  5481. dialects accepted include the current draft of the ANSI C++ standard
  5482. (including templates and exceptions), Cfront 2.1 C++, and AT&T 3.0 C++. To
  5483. assist in converting older C or C++ programs to ANSI C or C++, the compiler
  5484. also optionally accepts non-standard uses of C/C++ and provides warning
  5485. messages.
  5486. Apogee-FORTRAN 77/90 is an optimizing compiler accepting two dialects of
  5487. FORTRAN: the 1977 and 1990 ISO/ANSI standard. In addition, the compiler
  5488. accepts extensions found in Sun, IBM, VAX/VMS, Cray, and MIL-STD 1753 FORTRAN
  5489. 77s. The FORTRAN 90 mode is compliant with the ISO/ANSI FORTRAN 90 standard.
  5490. The Apogee-C/C++ and Apogee-FORTRAN 77/90 compilers work with several
  5491. programming tools that are offered as optional items on the same CD-ROM: the
  5492. TotalView graphical interface debugger from BBN, the SNiFF++ visual
  5493. programming environment from takeFive Software (C/C++ only), and the Sentinel
  5494. error detection programming tool from AIB Software. In addition, there is a
  5495. free GDB debugger on the CD-ROM modified by Apogee to work with C/C++ as well
  5496. as with FORTRAN.
  5497. Apogee compilers have been specifically tuned to take advantage of the
  5498. hardware features for each implementation of the SPARC architecture including:
  5499. SuperSPARC, hyperSPARC, MicroSPARCI, MicroSPARC II, and PowerUp. Apogee
  5500. compilers also let the user select the specific target processor by a compiler
  5501. switch. For more information contact Apogee Software Inc., 1901 S. Bascom
  5502. Ave., Suite 325, Campbell, CA 95008-2207, (408) 369-9001; FAX: (408) 369-9018;
  5503. E-mail: info@apogee.com.
  5504.  
  5505.  
  5506. IST And V.I. Corporation Announce X-Designer 4
  5507.  
  5508.  
  5509. Imperial Software Technology and V.I. Corporation have announced X-Designer 4,
  5510. a cross-platform GUI builder for Motif and Windows applications. Features of
  5511. X-Designer 4 include a Windows-compliant mode for Windows development and the
  5512. Microsoft Foundation Class Library as the Windows interface.
  5513. In Windows mode, X-Designer can generate MFC code, which can then be compiled
  5514. with native Windows tools, such as Visual C++. Also in Windows mode,
  5515. X-Designer indicates which Motif resources do not have an equivalent in
  5516. Windows and which existing X-designer Motif interface design files may be
  5517. loaded in X-Designer 4. X-Designer's toolbar has a Compliance button which is
  5518. marked with a red cross if the design contains non-Windows-compliant design
  5519. elements. Pushing this button displays a list of the non-compliant elements,
  5520. which may then be modified.
  5521. Other features of X-Designer 4 include: internationalization, drag and drop,
  5522. tear-off menus, the ability to generate C/C++ code, HyperText Help, geometry
  5523. management capabilities, an unlimited Undo button, and an intuitive Compound
  5524. String Editor. X-Designer 4 supports Sun, HP, Silicon Graphics, IBM, DEC, and
  5525. SCO platforms, with deployment on Windows supported by MFC.
  5526. X-Designer 4 is priced at $3,500 for the first license. Upgrades are free to
  5527. existing X-Designer users with a current support contract. For more
  5528. information contact Imperial Software Technology, Reading, United Kingdom, +44
  5529. 734-587055 or V. I. Corporation, Northampton, MA, (413) 586-4111.
  5530.  
  5531.  
  5532. Tartan Introduces Tartan C and Tartan C/C++ 2.0
  5533.  
  5534.  
  5535. Tartan, Inc. has introduced Tartan C and Tartan C/C++ 2.0 compilers for the TI
  5536. TMS320C3x and C4x digital signal processors (DSPs). Both the C and C++
  5537. compilers use the on-chip capabilities of TI's C3x and C4x DSPs, and are
  5538. switch-selectable for both of the processors.
  5539. Tartan C and Tartan C/C++ are development systems which include:
  5540. highly-optimized C (Tartan C) and C++ (Tartan C/C++) compilers, modular
  5541. runtime systems with a selective linker, a C3x/C4x assembler, floating-point
  5542. math libraries, object file utilities, and documentation. Both Tartan C and
  5543. Tartan C/C++ support the SPOX real-time DSP operating system from Spectron
  5544. Microsystems. SPOX provides a real-time, multitasking kernel and bridges to
  5545. other operating systems such as Windows and UNIX. In addition, Tartan C and
  5546. C/C++ both include the FasTar single-precision runtime math library, as well
  5547. as tools for building and maintaining real-time DSP applications.
  5548.  
  5549. Optional components for Tartan C and Tartan C/C++ include a source and
  5550. machine-level symbolic debugger and an instruction simulator. Native,
  5551. window-based debugger user interfaces are available for Microsoft Windows for
  5552. the PC and Sun OpenWindows for the SPARC. The VecTar and SigTar DSP math
  5553. libraries are also available for use with C and C/C++ compilers,
  5554. Tartan C is available for PC and Sun SPARC platforms. Prices for the Tartan C
  5555. compiler start at $1,495. Existing Tartan C/C++ customers with maintenance
  5556. will be upgraded automatically to version 2.0. For more information contact
  5557. Tartan Inc., 300 Oxford Dr., Monroeville, PA 15146, (412) 856-3600; FAX: (412)
  5558. 856-3636.
  5559.  
  5560.  
  5561. ViewSoft Releases UTAH 1.1
  5562.  
  5563.  
  5564. ViewSoft, Inc. has released UTAH 1.1, a C/C++ application and GUI builder.
  5565. UTAH 1.1 lets programmers create GUIs without adding interface dependencies to
  5566. their program objects or writing interface code. Included in UTAH 1.1 are
  5567. several GUI building technologies that automate the assembly, testing, and
  5568. maintenance of GUIs: the Editable Object System (EOS), Semantic Interface
  5569. Technology, and a User Extensible Toolkit.
  5570. The Editable Object System (EOS), which is a C++ class library with
  5571. platform-independent extensions, provides reflection (self-describing objects)
  5572. and run-time-binding. User objects inherit this extended functionality from
  5573. base EOS objects. The Semantic Interface Technology uses "smart components"
  5574. that eliminate interface dependencies for the program objects. UTAH
  5575. automatically performs type conversion and synchronization of interface
  5576. objects with program data whenever either interface or program variables
  5577. change. This automatic synchronization frees users from routine user-interface
  5578. event management, while preserving event access for performance or custom
  5579. control. UTAH's User Extensible Toolkit lets users create interactions which
  5580. are then used, like the components supplied with UTAH, to build complex
  5581. interactions without learning the interactor' s API or sub-classing to get
  5582. control. Existing VBX controls can be imported into UTAH and used as if they
  5583. were native UTAH components.
  5584. UTAH 1.1 for Windows is priced at $1,490. For more information contact
  5585. ViewSoft Inc., Provo, UT, (801) 377-0787.
  5586.  
  5587.  
  5588. Continuus Software Ships Continuus 4.0
  5589.  
  5590.  
  5591. Continuus Software Corporation has begun shipping Continuus 4.0, a
  5592. configuration management tool. Continuus 4.0 lets software development teams
  5593. coordinate and manage their activities, including change tracking, task
  5594. management, code development, testing, documentation, release management, and
  5595. ongoing maintenance.
  5596. Continuus 4.0 includes a Distributed Code Management (DCM) facility which lets
  5597. geographically dispersed development teams synchronize common project data,
  5598. whether or not the sites are linked by a network. DCM also includes a Vendor
  5599. Code Management utility which tracks software source or libraries received
  5600. from external suppliers, and merge them with local modifications. Also
  5601. included in Continuus 4.0 is a code migration facility that preserves a
  5602. project's original file structure, and automatically loads SCCS and RCS data.
  5603. Project migration can be performed both interactively and in batch mode.
  5604. Continuus 4.0 also includes a Motif GUI as well as a command line interface.
  5605. Other features of Continuus 4.0 include an "off-the-shelf" customizable
  5606. process model, which defines user roles, and ObjectMake, an object-oriented
  5607. Make facility. ObjectMake is compatible with common makefile formats, and
  5608. products built with ObjectMake can be automatically version-controlled and
  5609. shared by multiple users.
  5610. Continuus 4.0 supports major UNIX platforms. Continuus/CM and Continuus/PT are
  5611. available on a floating license basis. Continuus 4.0 is priced at $4,000 per
  5612. simultaneous user. For more information contact Continuus Software
  5613. Corporation, 108 Pacifica, Irvine, CA 92718, (714) 453-2200; FAX: (714)
  5614. 453-2276.
  5615.  
  5616.  
  5617. Tower Concepts Announces GlobalTrack and Ports Razor
  5618.  
  5619.  
  5620. Tower Concepts has announced GlobalTrack, a distributed processing software
  5621. module for Razor, the company's UNIX-based problem tracking and configuration
  5622. management tool suite. Razor is an integrated tool suite for developers,
  5623. combining a tailorable issue-tracking system with traditional version control
  5624. and build coordination capabilities. Using the Internet or other
  5625. X.400-compliant systems, GlobalTrack lets development teams in different
  5626. geographic locations coordinate their activities, priorities, and objectives
  5627. though synchronized issue-tracking databases using the existing e-mail
  5628. networks for communication.
  5629. In another notice, Tower Concepts announced a port of Razor to HP' s HP-UX and
  5630. Silicon Graphics' IRIX under the Motif GUI. Razor also supports Solaris and
  5631. SunOS.
  5632. GlobalTrack is free of charge to registered Razor users. Razor is priced at
  5633. $495 for a single floating license. An evaluation copy of the Razor manual is
  5634. also available. For more information contact Tower Concepts, Inc., 103 Sylvan
  5635. Way, New Hartford, NY 13413, (315) 724-3540; E-mail: razor-manual@tower.com or
  5636. ftp.uu.net.
  5637.  
  5638.  
  5639. HockWare Releases VisPro/C and VisPro/C++
  5640.  
  5641.  
  5642. HockWare Incorporated has released two OS/2 GUI development tools, VisPro/C
  5643. and VisPro/C++. Modeled after Vis-Pro/REXX, a visual REXX programming tool for
  5644. OS/2 2.x, VisPro/C and VisPro/C++ are object-oriented drag and drop tools for
  5645. IBM's User Interface Class Library and CSet compilers (CSet, CSet++, and
  5646. CSet++ FirstStep). VisPro/C and VisPro/C++ provide an integrated environment
  5647. that includes: the Build Options Editor, which allows programmers to visually
  5648. set compiler and linker options; the Build Monitor, which lets programmers
  5649. monitor the progress of compilation, linking, and resource compilation; and
  5650. the Resource Editor, which lets programmers define icons, bitmaps, and
  5651. strings.
  5652. VisPro/C and VisPro/C++ are integrated with WorkPlace Shell and generate
  5653. non-proprietary royalty-free C/C++ code compatible across VisPro products.
  5654. Other features of VisPro/C and VisPro/C++ include: a set of CUA '91 objects
  5655. supporting 3-D business graphics, formatted entry fields, spreadsheet, clock,
  5656. and calendar; a built-in visual DB2/2 database designer; a custom object
  5657. builder based on the IBM SOM; and multiple development views.
  5658. VisPro/C and VisPro/C++ are priced at $399 each. For more information contact
  5659. HockWare, Inc,, 315 N. Academy St., Suite 100, Cary, NC 27513, (919) 380-0616;
  5660. FAX: (199) 380-0757.
  5661.  
  5662.  
  5663. ZGRAF Announces ZGRAF C++ Graph Toolkit
  5664.  
  5665.  
  5666. ZGRAF Software Products has released the ZGRAF C++ Graph Toolkit for Windows
  5667. and Windows NT. The ZGRAF C++ Graph Toolkit is a library of C++ routines for
  5668. producing technical and business graphs. Graph styles include: X/Y, Bar, Pie,
  5669. Area, Ribbon, Scatter, Polar, Log, 2-D Function, 3-D Surface, and Smith Chart.
  5670. The library is object-oriented and uses C++, but both C and C++ users can use
  5671. the product.
  5672. The ZGRAF C++ Graph Toolkit is priced at $30 for the Personal Developer
  5673. Version or at $45 for the Commercial Developer Version. The Personal Developer
  5674. is for private usage only; the Commercial Developer Version gives professional
  5675. developers an unlimited distribution license with no royalties. Both versions
  5676. of the ZGRAF C++ Graph Toolkit include the C++ source code. For more
  5677. information contact ZGRAF Software Products, 1831 Old Hickory Court, New
  5678. Albany, IN 47150, (812) 949-9524; CompuServe: 70742,1356; Internet:
  5679. jjakob@delphi.com.
  5680.  
  5681.  
  5682. Excel Introduces Translator 1.0
  5683.  
  5684.  
  5685. Excel Software has introduced the Translator 1.0 reengineering utility to
  5686. enhance its suite of CASE tools. Translator 1.0 automates the generation of
  5687. diagrams within MacAnalyst and MacDesigner CASE tools from existing source
  5688. code. Translator 1.0 scans the source code and extracts information into a
  5689. text file which can be imported into MacAnalyst and MacDesigner.
  5690. Using Translator 1.0, a developer can document existing source by producing
  5691. diagrams and data dictionary information. Diagrams provide double-click access
  5692. to the related code. Object-oriented software written in C++ or Object Pascal
  5693. is translated to class diagrams using Booch, OMT, Shlaer/Mellor, or
  5694. Coad/Yourdon notation. The Class diagram illustrates each object class,
  5695. inheritance structures, and its attributes and operations. Code written in C,
  5696. Pascal, Basic, or FORTRAN can be used to generate structure charts. Diagrams
  5697. can also be organized into multiple diagram levels.
  5698. Translator 1.0 is priced at $495 for a single license. For more information
  5699. contact Excel Software, P.O. Box 1414, Marshalltown, IA 50158, (515) 752-5359;
  5700. FAX: (515) 752-2435; E-mail: casetools@aol.com.
  5701.  
  5702.  
  5703. Vireo Announces VToolsD
  5704.  
  5705.  
  5706. Vireo Software has announced VToolsD for Windows 3.1, a C/C++ toolkit for
  5707. developing Virtual Device Drivers (VxD). VToolsD includes QuickVxD, which
  5708. offers a "visual programming" model of VxD development, letting developers
  5709. create new VxDs by using the mouse. Libraries included with VToolsD enable VxD
  5710. developers to use Microsoft Visual C/C++ 32-bit Edition. The VToolsD C++ Class
  5711. Libraries provide a set of classes for those who prefer an object-oriented
  5712. framework for VxD development. On-line help, examples, and source code for all
  5713. of the VToolsD libraries are included.
  5714. VToolsD for Windows 3.1 is priced at $495. For more information contact Vireo
  5715. Software, Inc., 385 Long Hill Rd., Bolton, MA 01740, (508) 779-8352; FAX:
  5716. (508) 779-8351; Internet: vireo@vireo.com.
  5717.  
  5718.  
  5719.  
  5720. Pentek Introduces File Transfer Language
  5721.  
  5722.  
  5723. Pentek, Inc. has introduced File Transfer Language (FTL), a SCSI
  5724. file-management system, plus a collection of software libraries for
  5725. initializing, organizing, sorting, and retrieving data on SCSI devices. FTL
  5726. supports 21 SCSI devices connected to a VMEbus-based DSP and data acquisition
  5727. subsystem using Pentek's MIX-interface mezzanine boards. The SCSI hardware
  5728. interface is provided by the Model 4255 based on the MIX interface.
  5729. FTL consists of a UNIX-like multitasking environment with function libraries
  5730. that provide subroutines for using the SCSI file system. FTL supports two
  5731. programming environments, C and SPOX. The C libraries are subroutines that can
  5732. be used for single-threaded C programming or as a header file that may be
  5733. embedded in custom DSP applications. SPOX supports multitasking and provides
  5734. streaming drivers. SPOX also includes a modular and portable real-time
  5735. operating system for C, workstation interfaces, and a library of DSP
  5736. functions. The FTL also supports Pentek's SwiftNet, based on the TCP/IP
  5737. interface for Ethernet.
  5738. The FTL is priced at $2,000 and Model 4255 is priced at $1,795. For more
  5739. information contact Pentek, Inc., 55 Walnut St., Norwood, NJ 07648, (201)
  5740. 767-7100; FAX: (201) 767-3994.
  5741.  
  5742.  
  5743. TGS Ships OpenGL DHA
  5744.  
  5745.  
  5746. Template Graphics Software Inc. has begun shipping the beta version of OpenGL
  5747. DHA for Solaris and Open Inventor for Solaris. OpenGL is a 3-D graphics
  5748. software system licensed from Silicon Graphics. Open Inventor is a C++ class
  5749. library based upon OpenGL and is used for 3-D graphics application
  5750. development. OpenGL DHA for Solaris is a direct port of OpenGL to Sun. The
  5751. beta release is an OpenGL feature and conformance release for Sun GX and ZX
  5752. hardware.
  5753. For more information contact Template Graphics Software, Inc., 9920 Pacific
  5754. Heights Boulevard, Suite 200, San Diego, CA 92121, (619) 457-5359; FAX: (619)
  5755. 452-2547.
  5756.  
  5757.  
  5758. ISE Ships EiffelNet
  5759.  
  5760.  
  5761. Interactive Software Engineering, Inc. has begun shipping EiffelNet, a
  5762. client-server, application-development communication system. Using EiffelNet,
  5763. developers can build applications that exchange objects asynchronously over a
  5764. network using sockets. EiffelNet is a part of ISE Eiffel, an object-oriented
  5765. graphical development environment that is compatible with existing C software.
  5766. The Eiffel workbench generates stand-alone ANSI C. EiffelNet follows the
  5767. structure of other ISEEiffel libraries, and fits in the overall hierarchy the
  5768. fundamental library of data structures and algorithms of ISE Eiffel 3.
  5769. EiffelNet provides users with predefined schemes corresponding to the most
  5770. commonly occurring client-server cases. An application can then include a
  5771. client class and a server class that inherit from the corresponding predefined
  5772. classes. New classes can then build the specific objects to be exchanged and
  5773. specify the required processing. Communication and synchronization aspects are
  5774. handled automatically by the predefined classes. More specific classes are
  5775. also available for applications that need a finer degree of control of the
  5776. actual underlying mechanisms.
  5777. For more information contact Interactive Software Engineering Inc., 270 Storke
  5778. Rd., Suite 7, Goleta, CA 93117, (805) 685-1006; FAX: (805) 685-6869; E-mail:
  5779. info@eiffel.com.
  5780.  
  5781.  
  5782. Cadre Technologies Announces ObjectTeam/OOA Sim And Professional Services
  5783.  
  5784.  
  5785. Cadre Technologies Inc. has announced ObjectTeam/OOA Sim, a simulation and
  5786. verification tool for their ObjectTeam for Shlaer-Mellor tool suite.
  5787. ObjectTeam/OOA Sim lets developers verify, simulate, and prototype their
  5788. applications at the early stage of the development cycle. ObjectTeam/OOA Sim's
  5789. Motif-based GUI lets developers design test scenarios, execute the scenarios
  5790. while viewing the results as they happen, set break points, and display
  5791. summary information. ObjectTeam/OOA also supports incremental changes.
  5792. ObjectTeam/OOA Sim can simulate multiple subsystems and provide support for
  5793. both state transition diagrams and action data flow diagrams. ObjectTeam/OOA
  5794. Sim uses a high-level, object-oriented language that supports the
  5795. Shlaer-Mellor method's syntax and semantics.
  5796. In another notice, Cadre announced Professional Services to support their
  5797. development tools. Services include: product support, offering installation
  5798. assistance, integration services, and implementation support; education
  5799. services consisting of product and methodology training; and consulting and
  5800. project management services. Classes are offered on-site or at Cadre's
  5801. corporate training centers.
  5802. Pricing for ObjectTeam/OOA Sim starts at $10,000 per network. For more
  5803. information contact Cadre Technologies Inc., 222 Richmond St., Providence, RI
  5804. 02903, (401) 351-5950; FAX: (401) 455-6800.
  5805.  
  5806.  
  5807. Blinkinc Introduces Multi-Lingual Blinker 3.0
  5808.  
  5809.  
  5810. Blinkinc has introduced Multi-Lingual Blinker 3.0. Blinker 3.0, a royalty-free
  5811. DOS extender, Windows linker, and DOS dynamic overlay linker, that supports
  5812. English, German, Spanish, and Chinese. Foreign language versions of Blinker
  5813. 3.0 include a translated technical reference manual and Norton Guide help
  5814. file. Technical support is available in various languages.
  5815. Blinker 3.0 requires an 8086 microprocessor (or higher) to link or run
  5816. real-mode programs. Blinker 3.0's DOS extender requires an 80286
  5817. microprocessor (or higher) to run protected-mode programs and is compatible
  5818. with DPMI, VCPI, and XMS specifications. Protected-mode programs will run
  5819. under Windows, OS/2, and DOS.
  5820. Blinker 3.0 is priced at $299. For more information contact Blinkinc, 8001 W.
  5821. Broad St., Richmond, VA 23294, (804) 747-6700; FAX: (804) 747-4200; BBS: (804)
  5822. 747-73333.
  5823.  
  5824.  
  5825. QSi Ships ProTEST
  5826.  
  5827.  
  5828. Quality Systems International, Inc. has begun shipping ProTEST, a test
  5829. engineering tool which provides cross-platform portability and multiple
  5830. database access; a character or graphical user interface including Windows
  5831. v3.1; multi-user, on-line access and execution of test cases; an interface
  5832. between automated test tools and problem error tracking; and standard
  5833. management reports, along with integrated query and report writer.
  5834. Other features of ProTEST include: ability to link requirements to test cases
  5835. or test steps, user defined criteria for the test cases, priority setting for
  5836. test cases, user alerts for test cases requiring attention, and test status
  5837. summaries with calculations of the pass/fail percentage. ProTEST also includes
  5838. seven methods for printing test cases.
  5839. For more information contact Quality Systems International, Inc., 416-420
  5840. Highland Ave., Cheshire, CT 06410, (800) 557-9787 or (203) 699-9787; FAX:
  5841. (203) 699-9789.
  5842.  
  5843.  
  5844.  
  5845.  
  5846.  
  5847.  
  5848.  
  5849.  
  5850.  
  5851.  
  5852.  
  5853.  
  5854.  
  5855.  
  5856.  
  5857.  
  5858.  
  5859. We Have Mail
  5860. Dear C/C++ Users Journal:
  5861. I just bought the September 1994 issue of your magazine. On page 19, David
  5862. Singleton has an article on Windows memory management. However, this article
  5863. is so full of bugs as an article on this subject can possibly get. The first
  5864. line of the article states that "It is generally recomended that programs
  5865. developed to run under MS-Windows use the medium memory model." This is just
  5866. plain wrong. The recomended memory model for Windows programs is large. Medium
  5867. model was for real mode Windows.
  5868. But probably the worst error is the article's emphasis on GlobalLock and
  5869. GlobalUnlock. These functions are only needed for real mode Windows, and as we
  5870. all know, real mode Windows disappeared along with Windows 3.0. Windows 3.1
  5871. can move memory at any time without invalidating pointers. This, of course, is
  5872. due to its use of a Local Descriptor Table (LDT) to translate selector:offset
  5873. to a logical address.
  5874. In fact, Microsoft now recomends using malloc/free or new/delete (which maps
  5875. to malloc/free) for memory allocations. Both Visual C++ and Borland C++, and
  5876. probably the rest of the Windows C/C++ compilers, use a suballocation scheme
  5877. in malloc, similar to the one presented in the article. But even though the
  5878. article assumes real mode Windows, it actually requires protected mode to
  5879. work. On page 25, FarHeapBlock::GetHbFromHandle is defined as follows:
  5880. FP_FHB FarHeapBlock::GetHbFromHandle(HGLOBAL h)
  5881. {
  5882. FP_FHB p fhb = (FP FHB) GlobalLock (h);
  5883. ASSERT (p_fhb != NULL);
  5884. GlobalUnlock (h);
  5885. return p_fhb;
  5886. }
  5887. The GlobalUnlock call actually tells Windows that it's okay to move the block.
  5888. The pointer returned from the function is therefore invalid! It can be used
  5889. safely only in standard or enhanced mode, and in real mode only until the next
  5890. GetMessage or PeekMessage call. I find it amazing that a magazine starts off
  5891. its Windows Programming issue with such an outdated and erroneous article. The
  5892. only thing I got out of it was the name of a magazine to stop buying, and the
  5893. name of a consultant firm to avoid.
  5894. Martin Larsson, author of several unknown
  5895. utilities for DOS and Windows
  5896. e-mail: larsson@cs.colorado.edu
  5897. David Singleton replies:
  5898. Dear Editor,
  5899. Thank you for sending me a copy of Martin Larsson's letter. Interesting and
  5900. provocative reading. I give my response below, interlaced with excerpts from
  5901. Martin Larsson's original text [in italics]. In the interests of courtesy, I
  5902. am copying this reply to Larsson. If he would like to engage in further dialog
  5903. direct with me, I would be delighted to respond.
  5904. The recomended memory model for Windows programs is large. Medium model was
  5905. for real mode Windows.
  5906. I would ask Larsson to justify his statement that the recommended model for
  5907. Windows Programming is large. My justifications for my statement are (in no
  5908. particular order of importance):
  5909. a. There are probably still some people out there using Windows 3.0. We do
  5910. need to remember them.
  5911. b. The medium memory model allows one to run more than one instance of a
  5912. program. You cannot do this with the large model. (I will willingly concede
  5913. that there may be occasions when it might be disastrous to allow more than one
  5914. instance of a program to run.)
  5915. c. The Visual C++ V1.5 AppWizard utility, when it builds a program framework,
  5916. defaults to the medium memory model. It seems to me from this that Microsoft,
  5917. themselves, endorse the use of the medium memory model as the preferred
  5918. starting model.
  5919. d. Medium memory models are generally smaller and faster because near pointers
  5920. can be used to access the data in the local data segment.
  5921. e. Finally, I would refer to Charles Petzold's book Programming Windows 3.1,
  5922. published by Microsoft Press, Chapter 7, Page 285. His book is to all accounts
  5923. something of an authority on Windows programming. Petzold says: "Windows
  5924. programmers who require more than 64 KB of data in their programs might be
  5925. feeling a little nervous at this point. They have a right to be because the
  5926. compact and large models are not recommended for Windows programs. This does
  5927. not mean they can't be used however."
  5928. But probably the worst error is the article's emphasis on Global Lock and
  5929. Global Unlock.
  5930. Larsson has made a significant error in his statement concerning the use of
  5931. GlobalLock. It is perfectly true that there is no longer the need to
  5932. physically lock memory when using Windows 3.1. However, GlobalLock performs
  5933. one essential additional function that Larsson seems to have forgotten. It
  5934. translates the HGLOBAL returned by GlobalAlloc into a far pointer to the
  5935. allocated memory. It is certainly Microsoft policy to use GlobalLock in
  5936. conjunction with GlobalAlloc, as evidenced by the example code in Microsoft
  5937. KnowledgeBase articles Q77226 and Q74197.
  5938. In fact, Microsoft now recomends using malloc/free or new/delete (which maps
  5939. to malloc/free) for memory allocations.
  5940. If one is using a large memory model or a medium memory model with less than
  5941. 64 KB of data, then, of course, one should use malloc/free or new/delete. It
  5942. is by far the easiest way of getting more memory in these cases. However, if
  5943. one wishes to use a medium memory model and have more than 64 KB of data, then
  5944. one must use the Windows global memory services, as discussed in the article.
  5945. But even though the article assumes real mode Windows, it actually requires
  5946. protected mode to work.
  5947. I regret that Larsson has again made a small error. I quote from the Microsoft
  5948. SDK Programmers Reference Manual, Volume 2, concerning GlobalUnlock: "With
  5949. movable or discardable memory, this function decrements the object's lock
  5950. count. The object is completely unlocked and subject to moving or discarding
  5951. if the lock count is decreased to zero."
  5952. The emphasis above is mine. The marked phrase is the key to answering
  5953. Larsson's concern. Each FarHeapBlock holds references to the previous and next
  5954. FarHeapBlocks in the chain of FarHeapBlocks. I decided to hold these
  5955. references as HGLOBALs. To get the corresponding address, I needed to use
  5956. GlobalLock with an equivalent GlobalUnlock (it is always necessary to use
  5957. these as a pair) to get the corresponding address.
  5958. This is what the function FarHeapBlock:: GetHbFromHandle does for me. On entry
  5959. to this routine, I would expect the relevant global heap lock count to be 1,
  5960. as the HGLOBAL points to a block that has already been allocated. The call to
  5961. GlobalLock will increase the lock count to 2. The subsequent call to
  5962. GlobalUnlock will return the lock count to 1. As the function never causes the
  5963. lock count to go to zero (and hence unlock the block), Larsson's comment is
  5964. invalid
  5965. I look forward to seeing Martin Larsson's response to my points. I am prepared
  5966. to stand by my article and I believe that the above responses have refuted all
  5967. of his comments on the article.
  5968. Best wishes,
  5969. David Singleton
  5970. 100265,3625@compuserve.com
  5971. Larsson replies:
  5972. Here's my reply to David Singleton's comments.
  5973. There are probably still some people out there using Windows 3.0. We do need
  5974. to remember them.
  5975. So? The large model runs on Windows 3.0 too doesn't it? I will agree that
  5976. Windows 3.0 has a bug (how can I not?) that pagelocks all but the first
  5977. segment if you have multiple data segments. So, there can be no movement of
  5978. the segments. Not good. However, with Windows 95 coming up, I don't think we
  5979. should limit ourselves by bugs in Windows 3.0. It's as simple as saying, "The
  5980. program runs on Windows 3.0 and greater. However, if you experience 'out of
  5981. memory' errors on Windows 3.0, you might want to upgrade to Windows 3.1.
  5982. Windows 3.0 has a bug that..." You get the idea.
  5983. Remember, this only applies to programs with multiple writeable data segments.
  5984. And they can't use the medium model anyway. Also, remember that Windows is a
  5985. large model program. So when you use the medium model, you're in fact dealing
  5986. with mixed-model programming. This is, and has always been, a real pain since
  5987. you have to remember which pointers are far and which are near.
  5988. The medium memory model allows one to run more than one instance of a program.
  5989. You cannot do this with the large model.
  5990. This is simply not true. The limitation is that you cannot have more than one
  5991. read-write segment, as long as all segments but one are read-only. Multiple
  5992. instances works just fine in the large model (Programming at large, Dale
  5993. Rogerson, Microsoft Developer Network Technology Group). MS-compilers from 8.0
  5994. and up (VC++ 1.0), combine your data into one segment if possible. This means
  5995. that your large model program automaticaly can run multiple instances. Of
  5996. course, Borland and Watcom always did this.
  5997. The Visual C++ V1.5 AppWizard utility, when it builds a program framework,
  5998. defaults to the medium memory model. It seems to me from this that Microsoft,
  5999. themselves, endorse the use of the medium memory model as the preferred
  6000. starting model.
  6001. Read, "The C/C++ Compiler Learus New Tricks," by Dale Rogerson, written Aug.
  6002. 28, 1992, revised Jan. 27, 1993. The article is available on The Microsoft
  6003. Developer Network. The conclusions is that Microsoft C/C++ version 7.0
  6004. introduces new programming practices that facilitate the development of
  6005. applications for Windows version 3.1 in protected mode. Programmers can now:
  6006. Use the large memory model. Large-model programs are compatible with the
  6007. protected modes of Windows version 3.1 and can have multiple instances.
  6008. Use _fmalloc. With C/C++, _fmalloc, which is the large or model-independent
  6009. version of malloc, performs subsegment allocation and conserves selector
  6010. usage.
  6011. Medium memory models are generally smaller and faster because near pointers
  6012. can be used to access the data in the local data segment.
  6013. Microsoft Systems Journal, 1993, Volume 8, Number 10, Questions & Answers,
  6014. C/C++, last question, summary: "The upshot is: if you have a good C++ compiler
  6015. that supports Windows, don't fret over memory management. But do use the large
  6016. model... Of course, you can always get the memory in the medium model if you
  6017. really want, by declaring everything far, and allocating everything with
  6018. _fmalloc of farmalloc, but then you have all sorts of problems in C++ trying
  6019. to allocate objects with new so their constructors get invoked.
  6020. Why subject yourself to such torture? Anyone who tells you that performance is
  6021. slower in the large model is probably a frustrated assembly-language
  6022. programmer, not to be trusted. It's true, of course, it is slower. But not
  6023. much. Your users will probably never notice. If a particular section of your
  6024. code is performance-critical, you can always use near pointers, or even
  6025. rewrite it in assembly. The large model is simply the easiest way to get gobs
  6026. of memory."
  6027. Yes, this is on C++, but most of it applies to plain C also. The large model
  6028. just makes life so much easier.
  6029. Finally, I would refer to Charles Petzold's book Programming Windows 3.1,
  6030. published by Microsoft Press, Chapter 7, Page 285. His book is to all accounts
  6031. something of an authority on Windows programming.
  6032. I agree on one point, Petzold's book is a very good book on Windows
  6033. programming. But, chapter 7 was not updated for 3.1. Therefore, it should not
  6034. be accepted as the authority on Windows 3.1 memory management. I'm sorry, but
  6035. Petzold's just plain wrong. Ask the guys on comp.
  6036. os.ms-windows.programmer.memory what they think of Chapter 7 in Petzold's
  6037. otherwise exellent book. You'll get the idea.
  6038. Larsson has made a significant error in his statement concerning the use of
  6039. GlobalLock.
  6040. I guess I wasn't clear enough. My point is that there's no reason for locking
  6041. and unlocking memory all the time. Lock once, when you allocate, using malloc,
  6042. farmalloc, GlobalAllocPtr, or new. Unlock once, when you free the memory using
  6043. free, farfree, GlobalFreePtr, or delete. Now, there are exceptions, just as
  6044. with everything in Windows. For instance, you can allocate memory in a dialog
  6045. procedure and return it to 'the calling function. However, since DialogBox
  6046. only returns an integer, you'll have to return a handle (16-bit). You could of
  6047. course use a global or member data in C++. Or you could send a message to the
  6048. parent of the dialog.
  6049.  
  6050. As the function never causes the lock count to go to zero (and hence unlock
  6051. the block), Larsson's comment is invalid.
  6052. So, what you're saying is that you call GlobalLock and GlobalUnlock totally
  6053. unnecessary since you could just as well keep a copy of the pointer. Quite
  6054. interesting when you're using the medium model to gain speed. And, you have
  6055. memory locked all the time, quite contrary to what Petzold recommends. Of
  6056. course, Petzold's wrong, memory can be locked for the whole duration of the
  6057. program. But you seem to contradict yourself when you follow Petzold's advice
  6058. on memory model, but not on when to lock and unlock the memory you allocate.
  6059. I am prepared to stand by my article and I believe that the above responses
  6060. have refuted all of his comments on the article.
  6061. Well, I think I've made my points clearer. All references are to Microsoft
  6062. Developer Network CD #8.
  6063. Martin Larsson
  6064. Singleton replies:
  6065. Dear Larsson,
  6066. Thanks for your last message. As the messages seem to be getting rather long,
  6067. I will not go repeating everything. I will just repeat any necessary salient
  6068. points.
  6069. So? The large model runs on Windows 3.0 too doesn't it? I will agree that
  6070. Windows 3.0 has a bug... You get the idea.
  6071. Point noted, thanks
  6072. Remember, this only applies to programs with multiple writeable data segments.
  6073. And, they can't use the medium model anyways.
  6074. Agreed.
  6075. So when you use the medium model, you're in fact dealing with mixed-model
  6076. programming. This is, and has always been, a real pain...
  6077. Agreed. However, that is one nice thing about using MFC. Most of the time, one
  6078. does not need to worry about near and far pointers, although there are, of
  6079. course, occasions when one does need to remember which are which and that can,
  6080. I agree, sometimes be a pain.
  6081. The limitation is that you cannot have more than one read-write segment, as
  6082. long as all segments but one is read-only.
  6083. Correction accepted. That is really what I meant to say. In my article I was,
  6084. though, dealing with the case where one would want more than one segment's
  6085. worth of read-write data (i.e., more than 64 KB of data) and, in this case, my
  6086. assertion that one cannot have more than one instance of a program is correct.
  6087. Thanks for the references to the two Microsoft articles and for your comments
  6088. on Chapter 7 of Petzold's books. I shall add them to my data bank.
  6089. My point is that there's no reason for locking and unlocking memory all the
  6090. time.
  6091. I entirely agree with your comment. There is absolutely no need to lock and
  6092. unlock memory all the time. You raised this comment with reference to my
  6093. function FarHeapBlock::GetHbFromHandle. My reason for doing this is contained
  6094. in the article, where I say, "I use doubly linked lists to keep track of
  6095. individual FarHeapBlocks. Thus, a FarHeapBlock contains pointers both to the
  6096. previous FarHeapBlock and to the next FarHeapBlock. In the case of
  6097. FarHeapBlocks, I decided to store the HGLOBAL of the previous and next blocks,
  6098. rather than a far pointer."
  6099. Whenever I need to recover the actual address of a FarHeapBlock, I call
  6100. FarHeapBlock::GetHbFromHandle, using GlobalLock and GlobalUnlock to do the
  6101. necessary conversion. The fact that they set and release locks is, from this
  6102. function's perspective, a side-effect. What I want is the HGLOBAL to FP_FHB
  6103. conversion.
  6104. The article then goes on to say, "With hindsight, I could have used far
  6105. pointers instead. Indeed, this could probably give slightly more efficient
  6106. code." I am sure you would say that I could delete 'probably' and 'slightly'
  6107. from the above. With hindsight, I would agree with you.
  6108. I will close by thanking you for this fascinating discussion. It is always
  6109. good scientific and engineering practice to test one's arguments by open
  6110. debate. I too use the large model when I consider it appropriate. However, I
  6111. also use the method set out in my article, when that is appropriate.
  6112. Best wishes,
  6113. David Singleton
  6114. In fairness to David Singleton, I should report that we sat on his article for
  6115. a number of months before publishing it, then failed to check whether all the
  6116. information was still timely. We indulge in such antics less and less often,
  6117. and we check our accepted submissions better all the time, but we -- and our
  6118. authors -- will never get it perfect.
  6119. Windows, in particular, is a bottomless pit of complexity. The preceding
  6120. discussion reminds me of numerous debates from the distant past over how best
  6121. to perform various operations under various System/370 operating systems.
  6122. Microsoft has reinvented IBM a quarter century later. I cross my fingers every
  6123. time one of our authors describes one side of this latest elephant. They are
  6124. more courageous than I am. -- pjp
  6125. Bill,
  6126. Re-October 1994 C/C++ UJ: yet another fine issue... I have mixed, mainly
  6127. queasy, feelings over Bob Jervis's proposal for a (C++)--! It's true that C++
  6128. has grown like Topsy and even the experts are confused. (See, for example, the
  6129. theological altercations in any issue of The C++ Report.) But surely all
  6130. programming languages have had similar growing pains as we've struggled to
  6131. gain familiarity with "arcane" (i.e., new) concepts and features. Take the C
  6132. declaration syntax (please). And consider that after 20 years or so, we still
  6133. see "not quite accurate" CUJ articles using or explaining C
  6134. arrays-as-pointers. If one of Bob's motivations is to ease the
  6135. compiler-writer's burden, forget it. Compiler-writers, in rerum natura, thrive
  6136. on impossible challenges -- specification ambiguities being especially
  6137. welcomed (and provably unavoidable from the lessons of Algol 68). Otherwise,
  6138. pjp would be editing The Machine-Language Users Journal. My suspicion is that
  6139. as the C community mulls and fights over Bob's "minimalist" OOPL, his (C++)--
  6140. will post-accrete "one damn good thing after another,"aping the agonizing
  6141. stepwise refinement (some might call it unfinement) of Bjarne's original "C
  6142. with Classes" (1979) into ANSI/ISO C++ (1994). The story is uniquely
  6143. documented in The Design & Evolution of C++ (Bjarne Stroustrup,
  6144. Addison-Wesley, 1994), which traces the pros and cons of each increment.
  6145. Consider just one item not mentioned by Bob: the C++ "exposure" specifier
  6146. protected. This public/private "compromise" was added for members in Release
  6147. 1.2 and for base classes in Release 2.1. For member functions, Bjarne still
  6148. considers protected to be a "fine way of specifying operations for use in
  6149. derived classes." However, protected data members are now frowned on, even by
  6150. Mark Lipton who pushed for the change (ibid, p. 302). Will Bob exclude
  6151. protected completely as "inessential," or, learning from C++ experience,
  6152. confine it to member functions. I smell a good twelve months argufying on this
  6153. point alone. It seems that (C++)-- will never "catch up" since "All is Flux,"
  6154. especially C++ itself. PAX, etc.
  6155. Stan Kelly-Bootle
  6156. Contributing Editor,
  6157. UNIX Review and OS/2 Magazine
  6158. PS: Re-your name: the Celtic diphthong au is usually pronounced ow, as in
  6159. plow, not aw, as in plaudit. However, both sounds seem appropriate! BTW: Peter
  6160. van Linden offers $1 for each error found in his Expert C Programming. I
  6161. reported that he had spelled your name Plaugher on two occasions, but he only
  6162. sent me one dollar!
  6163. Whew! It might have been easier refereeing the previous discussion on Windows
  6164. programming than replying to a classic Kelly-Bootle missive. (C++)-- is a cute
  6165. name, but an invalid expression, in Standard C at least. More important, not
  6166. even Jervis is proposing making C a full-fledged OOPL. The idea is to crib a
  6167. bit of prior art that has proven utility at low cost in complexity, not to
  6168. invent yet another language on the fly as part of the standardization process.
  6169. As for my name, I'm told it originally hails from Alsace Lorraine. A slippery
  6170. dipthong like au shifts neatly between French and German pronunciation,
  6171. depending on who's in charge in a given year.
  6172. Dear Sirs!
  6173. My comments about the idea of the modifications to C mentioned in the article
  6174. "All is Flux" (CUJ, October 1994 by B. Jervis): YES! YES! YES! YES! YES! YES!
  6175. ... ad infinitum! The only problem with it: It may kill C++. Then again we
  6176. (real everyday programmers) may need such extended C (maybe we should call
  6177. C+-?) desparetely when C++ dies by itself under its own weight. C++ inflates
  6178. at the rate approaching the rates of supernovas.
  6179. Sure, it gives more and more employment for language commentators, critics,
  6180. academic programming gurus, etc. (This is not an innuendo about PJP. Please
  6181. believe me!). Anyone remember Algol 68? If we needed a language so big as the
  6182. future C++ maybe we should settle for something already working and tested
  6183. like Ada? C became so popular because it allowed people to program close
  6184. enough to the performance levels of assembly language with benefits of the
  6185. structure of a higher-level programming language. C++ became popular only
  6186. because it is perceived as an extension of C!
  6187. This is of course my personal view, but then again I am being told from time
  6188. to time that true OOP can happen only in languages like Smalltalk and so on.
  6189. B. Jervis! Go on!
  6190. Bogdan M. Baudis
  6191. Innuendo or no, I do get a certain job security from the ambitions of language
  6192. designers. Still, I wish sometimes the job of explaining esoterica wasn't
  6193. quite so hard. --pjp
  6194. C/C++ Users Journal
  6195. This is the second of two letters. The first, which is enclosed behind this
  6196. one, was waiting to be stuffed in its envelope when my copy of the October
  6197. issue of the Journal arrived. Needless to say, I read Bob Jervis's article and
  6198. your sidebar with great interest. Having done that, I feel that what I said in
  6199. my first letter concerning ADTs versus OOPing, and C's capacity to support new
  6200. dialects, still needs saying -- perhaps more than ever.
  6201. If classes are added to C, that's fine. That is, it is if they don't get in
  6202. the way of their ADT forebears. My concern is that although there are many
  6203. instances where another level of sophistication is needed, anything which
  6204. creates the impression that OOPing is the way could stifle the growth of C as
  6205. a source of new programming ideas. Also, if the freedom to create and expand
  6206. ADT libraries is legislated against by committee fiat, I, for one, will not
  6207. use any compiler implementing that standard.
  6208. I've studied more than a few articles over the years on how to OOP in C,
  6209. including the one Colvin did in CUJ last year. I've also "poured over" more
  6210. than one book on the subject, and I have to say that I've yet to find any
  6211. well-wrought discussion of the relationship between ADTs and classes as clone
  6212. factories. The nail still protrudes!
  6213. On a related point: In his Letter to the Editor in this October issue, someone
  6214. named Marty says: "One of the best ways to become a better programmer is to
  6215. read other people's code,..." This is a fairly common, often enunciated
  6216. belief, but one which I find actually qualifies as a half-truth. Reading other
  6217. people's code can also be one of the worst ways to learn. Not only are there
  6218. many ways to write correct C, the ways that apear in print tend to perpetuate
  6219. what might be called The-Old-Boys' version.
  6220. A full-fledged discussion of C's several prevailing styles and the effect they
  6221. have on program semantics ought to make an excellent subject for one of your
  6222. columnist's columns. It ought also to fit-in nicely with your expressed intent
  6223. to cover further discussion of C's ongoing development.
  6224. Truly,
  6225. Mark Rhyner
  6226. Dear Mr. Plauger:
  6227. I would like to suggest that a "read_only" data member access specification be
  6228. added to the public, protected, and private access specifiers in the C++
  6229. standard.
  6230. I find myself writing numerous get_XXX functions to allow users of my classes
  6231. to access the private data in them. The data is declared private to localize
  6232. the responsibility and maintain the integrity of the data, but I still wish
  6233. the callers to be able to act on the data. The only other alternative I can
  6234. think of is to make the data public, which is okay if the users are
  6235. disciplined about not altering the data.
  6236. Problems with read_only might arise if the data exposed is a pointer, but that
  6237. is the way of C anyway. If you have any suggestions on how to deal with this
  6238. issue in C++ as it stands, I would be very interested.
  6239. On another subject, I would be very interested in reading a regular column in
  6240. The C/C++ Users Journal on programming style. It could cover issues such as
  6241. the above:
  6242. When to derive a new class as opposed to when a class should contain another
  6243. class
  6244. When should functions be declared virtual?
  6245. What are the relative merits of iostreams compared to fprintf?
  6246. What are the trade-offs among the possible design decisions?
  6247. Sincerely,
  6248. David Rosenbush
  6249. I agree that read-only access to certain member objects could save some
  6250. tedious writing. I also agree that style is an important topic. So far, we've
  6251. kind of smeared responsibility for discussing issues of style among all our
  6252. columnists. -- pjp
  6253.  
  6254.  
  6255.  
  6256.  
  6257.  
  6258.  
  6259.  
  6260.  
  6261.  
  6262.  
  6263.  
  6264.  
  6265.  
  6266.  
  6267.  
  6268.  
  6269.  
  6270.  
  6271.  
  6272.  
  6273.  
  6274.  
  6275.  
  6276.  
  6277.  
  6278.  
  6279.  
  6280.  
  6281.  
  6282.  
  6283.  
  6284.  
  6285.  
  6286.  
  6287.  
  6288.  
  6289.  
  6290.  
  6291.  
  6292.  
  6293.  
  6294.  
  6295.  
  6296.  
  6297.  
  6298.  
  6299.  
  6300.  
  6301.  
  6302.  
  6303.  
  6304.  
  6305.  
  6306.  
  6307.  
  6308.  
  6309.  
  6310.  
  6311.  
  6312.  
  6313.  
  6314.  
  6315.  
  6316.  
  6317. Embedding on a Budget
  6318.  
  6319.  
  6320. Jeff D. Pipkins
  6321.  
  6322.  
  6323. Jeff D. Pipkins is a senior systems engineer at Compaq, where he writes
  6324. firmware for intelligent option boards. He has been programming in C since
  6325. 1985, and various assembly languages since 1979. His current interests include
  6326. systems software tools, embedded OS's, and building small projects with
  6327. embedded microcontrollers. He can be reached via Internet mail at
  6328. pipkins@bangate.compaq.com.
  6329.  
  6330.  
  6331.  
  6332.  
  6333. Introduction
  6334.  
  6335.  
  6336. Let's say you want to burn some bits. You want to etch your code into the
  6337. silicon, but you want to avoid massive collateral damage to your wallet.
  6338. Fortunately, it is possible to embed C code into ROM without purchasing
  6339. expensive, specialized tools. This small embedded project will get you
  6340. started.
  6341. To keep the cost down on the software tools, I used the Microsoft C compiler
  6342. and assembler, simply because that's what I happened to have on disk at the
  6343. time. You could use other compilers instead with a little modification to the
  6344. code presented. You'll also need a device programmer to burn those bits into
  6345. your PROM, EPROM, Flash ROM, or whatever you choose. I've seen them for as
  6346. little as $130.
  6347. I could have chosen an exotic target platform, but to conserve cash, I chose
  6348. some inexpensive hardware that I had lying around the house. It's an old IBM
  6349. XT, sporting a classic 8088 processor at 4.77 MHz. There actually are some
  6350. good reasons for choosing such a machine, beyond simple economics. Several
  6351. processors aimed at the embedded market are basically composed of an 8088 core
  6352. augmented with on-chip devices. The 80188 has become well-established as an
  6353. embedded processor, and it's available in a seemingly endless variety of
  6354. incarnations: the 80C188XL, 80C188EA, 80C188EB, and 80C188EC, as well as
  6355. low-voltage and 16-bit versions of these, some so new that this year's
  6356. databook on them is marked "preliminary!" So if you want to get in on today's
  6357. embedded world, just grab yesterday's computer dinosaur at a ridiculously low
  6358. price and go for it!
  6359.  
  6360.  
  6361. A Simple Project
  6362.  
  6363.  
  6364. The purpose of this article is to show how to embed C code without expensive
  6365. tools. I present a small program to be embedded (Listing 1), along with the
  6366. other pieces needed to make it work. The program is a slight variation on the
  6367. time-honored "Hello World," modified to work in an embedded environment.
  6368. The built ROM image works without BIOS, MS-DOS, or anything else. It's
  6369. completely self-sufficient, and takes control immediately when the machine is
  6370. powered on.
  6371. The make file (Listing 2) handles a large part of the embedding task in this
  6372. project; it invokes the linker, exe2bin, and even debug to build a ROM image
  6373. capable of sending a greeting out a serial port. I explain this process in
  6374. detail later.
  6375. Another piece of code is required to get an image running once it's embedded
  6376. in ROM. This custom assembly-language startup code (Listing 3) sets up the
  6377. run-time environment needed for hello.c to run. Rather than give a
  6378. line-by-line account, I provide here a general explanation of what this code
  6379. must do. Finally, I've written a really stripped-down version of stdio. c,
  6380. which supports output to a serial port. My stdio.c, is not listed here, but is
  6381. available on this month's code disk.
  6382.  
  6383.  
  6384. Building the Program
  6385.  
  6386.  
  6387. For many programmers, the build process is just a tedious detail. However, if
  6388. you want to build a ROM image, you need to be just as skilled at building code
  6389. as you are at writing it. After all, the compiler is just one tool in the
  6390. chain. To build the ROM image, I also use the linker, exe2bin, and debug.
  6391. Chances are you' ve used all of these before in a cookbook fashion, but now
  6392. you'll need to take a closer look at what these tools actually do behind the
  6393. scenes so you can use them for the atypical task of creating a bootable ROM
  6394. image.
  6395.  
  6396.  
  6397. Poor Man's Locator
  6398.  
  6399.  
  6400. When you're writing MS-DOS programs, you usually don't have to worry about
  6401. where your code will be loaded, or even whether it's relocatable. Relocatable
  6402. code can be executed at any address, because it has no references to absolute
  6403. addresses. COM files are good examples of relocatable code. You can just copy
  6404. COM files anywhere, set up the segment registers, and jump in. The code in EXE
  6405. files is not always relocatable -- it may contain absolute references to code
  6406. and data. So how does MS-DOS load EXE files wherever it wants? The EXE file
  6407. contains a relocation table (sometimes called a "fix-up" table). This table
  6408. shows all of the absolute references in the image. After the loader copies the
  6409. image into RAM at a particular location, it adds the segment address of that
  6410. location to every absolute reference in the image. This process has been
  6411. called "locating," "relocating," "performing fix-ups," or "address binding".
  6412. (See Figure 1.)
  6413. If an EXE file's relocation table is empty, then there are no absolute
  6414. references, and the code is relocatable. Such a file can be converted into a
  6415. COM file (which is just a straight, relocatable image) using the exe2bin
  6416. program. (exe2bin is readily available; various versions have shipped with
  6417. MSDOS, the MSDOS technical reference manual, MSC, and MASM.) exe2bin's
  6418. original purpose was just to convert relocatable EXEs into COM files, and if
  6419. the EXE file had any entries in its relocation table, exe2bin would just
  6420. complain and give up. Later versions of exe2bin are more useful. If it finds
  6421. entries in the relocation table, it prompts you for a base address, and then
  6422. uses it to perform the fix-ups!
  6423. So exe2bin now does basically the same thing that the MS-DOS loader does,
  6424. except that it writes the resulting bound image to a file instead of executing
  6425. it. exe2bin will serve as a "poor man's locator." A more fanciful locator
  6426. would allow us to specify a different base address for each different segment,
  6427. so that the image would not have to be contiguous. Since segment information
  6428. is not contained in the EXE file, such a product generally has to do the
  6429. linking as well just to keep that information. That's why they're sometimes
  6430. called "Linker/Locators." An alternate approach might be to parse this
  6431. information from a MAP file that's generated by the linker.
  6432.  
  6433.  
  6434. Linking
  6435.  
  6436.  
  6437. According to a tradition older than the 8088, the C compiler divides a program
  6438. into segments. The compiler usually creates a code (or "text") segment, a data
  6439. segment for ininitialized data, a "bss" segment for unitialized data, and a
  6440. stack segment. MSC adds several other segments, such as a "null" segment, to
  6441. which null pointers point, a "const" segment for constants, and various
  6442. segments for relatively new inventions, such as the "far heap."
  6443. The linker combines like segments from many object modules. (See Figure 2.)
  6444. Even though each object module may have its own code segment, the linker
  6445. combines them all into a single contiguous segment. In addition, the linker
  6446. allows object modules to refer to code or data in other modules by linking
  6447. external references together, which of course is where the linker gets its
  6448. name.
  6449. The linker also determines the order of the segments in the final image. In
  6450. this project, the startup code must be the first object module for input to
  6451. the linker (as shown in the make file, line 52). The linker maintains the same
  6452. segment order that first appeared in the first file. I make use of this
  6453. convention to control where the segments are loaded in relation to each other.
  6454. Controlling this order is very important, since this project requires the
  6455. segments to be in a different order than usual.
  6456.  
  6457.  
  6458. Segment Order for Embedded Code
  6459.  
  6460.  
  6461. Moving your code into ROM places additional constraints on where things need
  6462. to be, and it's the job of the startup code (Listing 3) to see that everything
  6463. is in its place.
  6464. Before the C code executes, its data segments must be copied into RAM. The
  6465. executable code must remain in ROM so that it won't consume RAM space. For
  6466. this reason, all data must initially occupy a block located at the beginning
  6467. (lowest) location in the ROM image, so that all data offsets will be correct
  6468. when DS is set to the beginning of the RAM data area. When exe2bin asks for a
  6469. fix-up base, the make file supplies the segment address of the RAM data area
  6470. where the data will be after the startup code copies it. This will make far
  6471. data pointers correct. Since the first 256 bytes of RAM are reserved for the
  6472. interrupt vector table, I chose to put the RAM data area at 0x40:0, which is
  6473. just above that.
  6474. Since the code stays in ROM and the data is copied to RAM, the image becomes
  6475. discontiguous. This creates a new problem in that now, instead of just one
  6476. fix-up base, we really need two -- one for the code and one for the data.
  6477. Unfortunately, exe2bin allows only one fix-up base to be specified because it
  6478. assumes that the image will be contiguous when it executes. If we specify the
  6479. RAM data area segment address as the fix-up base, then far code pointers will
  6480. be incorrect, and if we specify the ROM as the fix-up base, then far data
  6481. pointers will be incorrect. I deal with this dilemma by using the compact
  6482. memory model, which allows up to 1 MB of data but only 64 KB of code. Thus,
  6483. all generated code is relative to a single CS register value, which will work
  6484. as long as the code contains no far calls or jumps (no far functions, and no
  6485. far function pointers).
  6486.  
  6487.  
  6488.  
  6489. More on Segment Order
  6490.  
  6491.  
  6492. As already mentioned, a program's data segments must be copied to RAM before
  6493. the program executes. The order in which these segments end up in RAM is also
  6494. important. Most of the segments will belong to what is known as a group, which
  6495. is a bunch of segments that will all be referenced using the same segment
  6496. register at run time. The compiler assumes there will be a group called
  6497. DGROUP, which is referenced by the DS register. The NULL segment is first so
  6498. that DS:0 will point to it, and the BSS segment and stack are at the end, so
  6499. that the near heap (if there is one) and stack can contend for space (this is
  6500. called a "parasitic" heap, since it feeds on stack space). However, there is
  6501. no near heap in this example. A heap exists only as supported by the library,
  6502. and the program can't use the malloc that comes with the compiler because it
  6503. depends on MS-DOS calls.
  6504. If you need a heap, you can write your own malloc and free. Should you decide
  6505. to do this, I'd recommend inserting another segment between BSS and stack to
  6506. make it easier to reference the end of the BSS segment. For this example, the
  6507. stack is part of DGROUP and SS == DS. If you prefer, you can throw a compiler
  6508. switch that removes the SS == DS assumption, and then remove the stack from
  6509. DGROUP. Removing the stack from DGROUP would give you more room for near
  6510. variables, but you'd have to make sure you set up SS properly. Finally, the
  6511. far data belongs after the stack, possibly out of reach of the DS register.
  6512.  
  6513.  
  6514. Execution -- Eventually
  6515.  
  6516.  
  6517. To me, part of the excitement of using an embedded system is being on your
  6518. own. No power-on self test, BIOS, or OS will get control before your code.
  6519. There's no one else to lean on. The processor comes to life, and you're in
  6520. charge. This is all great fun, but it imposes still more constraints on how
  6521. the program's constructed and loaded.
  6522. When the processor powers up or resets, it begins execution at F000;FFF0,
  6523. which is the last !6-byte paragraph of address space (see sidebar for
  6524. differences in 286 or later processors). The ROM image must have code
  6525. strategically located in this spot. Since the program has only 16 bytes to
  6526. work with, it will have to jump elsewhere, and since elsewhere may be more
  6527. than 64 KB away, the program must do a far jump. Recall, however, that far
  6528. jumps won't work -- they won't get fixed-up correctly. So this time I just
  6529. cheat by using debug to patch in a jump instruction to an absolute location.
  6530. The jump requires no fix-up because the address is absolute, and exe2bin
  6531. doesn't complain because I use debug after exe2bin (see Listing 2, line 74).
  6532. Now the problem is, how can I supply an absolute code location for the far
  6533. jump, since its location depends on where the data ends? I solve this problem
  6534. by putting a small amount of code at the very beginning of the ROM image.
  6535. Since the beginning of the ROM image is a constant, it's easy to insert a jump
  6536. to that location. The data is also located at the beginning of the ROM image,
  6537. and the NULL segment must be first. By default, when the MSC startup code and
  6538. linker create the NULL segment they initialize the first 16 bytes with zeros.
  6539. You could suppress these zeros (via a linker switch), but that's probably not
  6540. a good idea. Having zeros at the beginning of the NULL segment makes a program
  6541. a bit safer -- dereferencing a null pointer will access the NULL segment
  6542. instead of the data segment, as long as the access is no more than 16 bytes.
  6543. I've just given compelling reasons why both the NULL segment and the startup
  6544. code should occupy the beginning of the image. Will this work? Luckily, yes.
  6545. Unlike many processors, the 80x86 does not use a zero byte for a NOP
  6546. instruction. Instead, two zero bytes form the "add [bx+si], al" instruction,
  6547. so if the NULL segment is executed as code, it will appear to start with eight
  6548. of these instructions. Executing these instructions causes no harm, since
  6549. memory will be initialized afterwards anyway. I can place "real" code right
  6550. after the eight adds. This code, which I call jumpstart code, is actually
  6551. located in the NULL segment (I specify its location by enclosing it in a
  6552. segment . . . ends block -- see Listing 3, lines 49 through 100) so that the
  6553. linker can't possibly separate it from the 16 zeros.
  6554. The jumpstart code just needs to accomplish one thing, a far jump to the
  6555. startup code proper (_TEXT:0). That doesn't sound difficult, but recall that
  6556. exe2bin will add a base address (in this case, 0x40) to all absolute
  6557. references! It doesn't know the difference between absolute code references
  6558. and absolute data references, it just adds! The reference will be wrong at
  6559. execution time, but I know the base (0x40), so I can subtract it to undo the
  6560. effect of the incorrect fix-up. This locates the reference with respect to
  6561. zero, so I must also do the correct fix-up at run time to locate the reference
  6562. with respect to the beginning of the ROM image. Since self-modifying code is
  6563. out of the question (morals aside, we are executing in ROM), I use the first
  6564. doubleword at DS:0 temporarily for a jump vector, and compute the correct
  6565. address there. Since I'm using the compact memory model, I know that the
  6566. compiler won't generate any far jumps, and the trouble ends here. From this
  6567. point on, it's smooth sailing into the startup code, and eventually, into your
  6568. program.
  6569.  
  6570.  
  6571. Have Fun
  6572.  
  6573.  
  6574. I've presented a dirt cheap way to develop programs on a PC and embed them in
  6575. ROM. I hope you don't just read the code -- do something! Even if you don't
  6576. have an embedded project in mind, doing something simple is worth it just for
  6577. the experience and the education you'll get on the way. So snag yourself some
  6578. hardware, any hardware, and teach it to say hello to the embedded world.
  6579. ROM Startup on 80286 or Later Processors
  6580. If you just want to use a 80286 or better as a fast 8088, (in real mode, that
  6581. is, since you need more advanced software tools than the ones presented here
  6582. if you want to get into protected mode) you must be aware of some subtle
  6583. differences in its reset process.
  6584. When execution begins on a 80286 or higher, CS:IP will be F000:FFF0, just as
  6585. for the 8088, but address lines 20 and higher will stay high until the first
  6586. time the CS register is loaded. So execution begins at the top of the address
  6587. space, but if you do a far jump, or otherwise cause CS to be loaded, you'll
  6588. suddenly find yourself down in the first 1MB of address space! On most
  6589. architectures, the ROM is reflected into the top of the 1MB address space by
  6590. the address decode circuitry so that this isn't a problem. If your hardware
  6591. doesn't reflect the ROM, then you should use only near jumps, initialize
  6592. briefly, and use protected mode -- in fact you should get into protected mode
  6593. as soon as possible.
  6594. Figure 1 The loader adds the base address of the image to each of the
  6595. references mentioned in the relocation table.
  6596. Figure 2 The linker combines like segments from many object files. The segment
  6597. order is determined from the first object file.
  6598.  
  6599. Listing 1 The application to be embedded
  6600. /*===========================================================*\
  6601.  
  6602. hello.c
  6603.  
  6604. \*-----------------------------------------------------------*/
  6605.  
  6606. #include "stdio.h"
  6607.  
  6608. #define PUBLIC
  6609. #define PRIVATE static
  6610.  
  6611. /*===========================================================*\
  6612.  
  6613. main()
  6614.  
  6615. \*-----------------------------------------------------------*/
  6616.  
  6617. PUBLIC void main(void)
  6618. {
  6619.  
  6620. /*--------------------------------------------------------*\
  6621. Initialization
  6622. \*--------------------------------------------------------*/
  6623.  
  6624. init_stdio();
  6625.  
  6626. /*--------------------------------------------------------*\
  6627. Display a greeting...
  6628. \*--------------------------------------------------------*/
  6629.  
  6630.  
  6631. puts("Hello, embedded world!");
  6632.  
  6633. /*--------------------------------------------------------*\
  6634. If you return from main(). the startup code should
  6635. invoke a reset...
  6636. \*--------------------------------------------------------*/
  6637.  
  6638. puts("About to reset. Goodbye!");
  6639.  
  6640. }
  6641.  
  6642. /*===========================================================*\
  6643. End of source file.
  6644.  
  6645. \*===========================================================*/
  6646.  
  6647. /* End of File */
  6648.  
  6649. Listing 2 The make file
  6650. ########################################################################
  6651. #
  6652. # makefile for embedded "hello.c" application.
  6653. #
  6654. # MSC 7.0 Compiler Switches
  6655. #
  6656. # /c - compile only. no link step
  6657. # /AC - Specifies compact memory model
  6658. # /FPi87 - Generate in-linex87 code (but our code uses no fp instructions)
  6659. # /Gs - Remove Stack Overflow Checks
  6660. # /Ois - Optimize for code size over execution time.
  6661. # The 'i' allows certain functions (inp, outp, etc.) to be
  6662. # generated inline. Just say NO to unsafe optimization options!
  6663. # /W3 - Almost maximum warning level
  6664. # /Fc - Generate mixed C/asm listing to specified file
  6665. #
  6666. #
  6667. # LINKING
  6668. #
  6669. # Our code doesn't use any floating point variables at all, so
  6670. # it shouldn't need any floating point support. The -FPi87 option
  6671. # is used, and the clibc7 library is used just to make sure that
  6672. # no floating-point emulator code is included in the build.
  6673. #
  6674. ########################################################################
  6675. CFLAGS = /c /AC /Gs /Ois /FPi87 /W3
  6676.  
  6677. .c.obj:
  6678. cl $(CFLAGS) -Fc$*.cod $*.c
  6679.  
  6680. .asm.obj:
  6681. masm /MX $*.asm,$*.obj.$*.1st;
  6682.  
  6683. default: embed.bin
  6684.  
  6685. #
  6686. # Source to be compiled or assembled.
  6687. #
  6688.  
  6689. hello.obj: hello.c stdio.h
  6690.  
  6691.  
  6692. stdio.obj: stdio.c stdio.h
  6693.  
  6694. startup.obj: startup.asm
  6695.  
  6696. #
  6697. # Link step -- NOTE: startup MUST be linked first.
  6698. #
  6699.  
  6700. embed.exe: startup.obj hello.ebj stdio.obj
  6701. link startup hello stdio, embed.exe, embed.map /NOI/MAP/NOPACKCODE;
  6702.  
  6703. #
  6704. # In this step. we use exe2bin as a locator. It will ask for a
  6705. # segment base to use for fix-ups. We specify the segment address
  6706. # in RAM where the data will be during execution. The startup
  6707. # code will use the value specified here to decide where to copy
  6708. # the data before beginning execution. Value specified is in hex.
  6709. #
  6710.  
  6711. embed.fix: embed.exe
  6712. exe2bin embed.exe embed.fix < <<
  6713. 40
  6714. <<
  6715.  
  6716. #
  6717. # This step uses debug to insert code into the reset vector.
  6718. # Note that debug will load the first byte at offset 100h instead
  6719. # of offset 0. The instructions inserted are CLI, CLD, and JMP F800:0
  6720. #
  6721.  
  6722. embed.bin: embed.fix
  6723. debug embed.fix < <<
  6724. n embed.bin
  6725. r cx
  6726. 8000
  6727. e 80f0 fa fc ea 00 00 00 f8 00 00 00 00 00 00 00 00 00
  6728. w
  6729. q
  6730. <<
  6731.  
  6732. #
  6733. # end of makefile
  6734. #
  6735.  
  6736. Listing 3 Startup code
  6737. ;======================================================================
  6738. ;
  6739. ; Start-up code for embedded C programs
  6740. ;
  6741. ; intended for use with Microsoft C, but porting it to Borland C
  6742. ; or other similar compilers shouldn't be too difficult.
  6743. ;
  6744. ; Donated to the public domain by Jeff D. Pipkins, who of course is
  6745. : not liable for damages...
  6746. ;
  6747. ;
  6748. ; This file MUST be linked FIRST. The linker will combine segments
  6749. ; with the same class name together, and it will order those combined
  6750.  
  6751. ; segments in the order that they first appear in this file. This
  6752. ; is extremely important. It is always the first file the linker
  6753. ; sees that gets to choose the ordering of the segments.
  6754. ;
  6755. ;----------------------------------------------------------------------
  6756. ROMSEG EQU 0F800h ; beginning of ROM
  6757. STACKSIZE EQU 14096
  6758.  
  6759. ;----------------------------------------------------------------------
  6760. ; data group (to fit in 64K)
  6761. ;
  6762. ; For our model, we need the data to appear first in the image,
  6763. ; with the code up above it. Unfortunately, we also need for
  6764. ; execution to begin at a fixed location known at build time,
  6765. ; and exe2bin wants that location to be either at offset 0 or
  6766. ; 100h from the beginning of the image. We solve this problem
  6767. ; by putting a little code in the front of the data segment that
  6768. ; can figure out where to jump.
  6769. ;
  6770. ;----------------------------------------------------------------------
  6771.  
  6772. DGROUP group NOTSTACK, NULL/, CONST/, _DATA/, _BSS, STACK
  6773. assume cs:DGROUP, ds:DGROUP
  6774. public jumpstart
  6775.  
  6776. ;The linker complains if there's no stack, and exe2bin
  6777. ; complains if there is. This segment is here to fool
  6778. ; the linker. See also the STACK segment. where exe2bin
  6779. ; is fooled.
  6780.  
  6781. NOTSTACK segment byte stack 'NOTSTACK'
  6782. ; This must remain empty.
  6783. NUTSTACK ends
  6784.  
  6785.  
  6786. NULL segment para public 'NULL'
  6787.  
  6788. ; The following bytes are the first bytes in the image.
  6789. ; MSC wants 16 bytes of 0's here.
  6790. ; We need to begin execution here as well...
  6791.  
  6792. jumpstart:
  6793. dw 8 dup (0) ; 8 x add [bx+si], al
  6794.  
  6795. ; There's already code in the reset vector to
  6796. ; do a cli & cld, but it's here again just for
  6797. ; emphasis, and more importantly, in case somebody
  6798. ; decides to modify the reset vector in the future.
  6799.  
  6800. cli ; interrupts off -- no stack yet, no vectors yet.
  6801. cld ; initialize direction flag
  6802.  
  6803. ; Compute segment address of "startup" label
  6804. ; The build process "locates" (provides fix-ups for) the
  6805. ; image so that it will run properly if it is loaded into
  6806. ; a particular address in RAM. This is what we want, since
  6807. ; our data will be copied into RAM. Unfortunately, we
  6808. ; really need for our code segment to be "located" with
  6809. ; respect to the beginning of ROM instead. We resolve
  6810.  
  6811. ; this dilemma in favor of the data, and then we'll set
  6812. ; the value of CS by hand. This way, the code won't
  6813. ; know that it's in the wrong place. This illusion will
  6814. ; all crash if anyone does a far jump, since the value
  6815. ; for CS will have been fixed-up incorrectly! So, no
  6816. : far jumps anywhere! The only exception to this follows.
  6817. ; We get away with it because we compensate for the
  6818. ; miscalculation in advance. (All of this hokey stuff is
  6819. ; a consequence of using exe2bin for a locator.)
  6820.  
  6821. mov dx, _TEXT ; Segment address of code segment
  6822. mov ax, ROMSEG ; Should be same as CS for right now
  6823. add dx, ax ; Add fix-up for code segment
  6824. mov ax, DGROUP ; Segment address where data will be
  6825. sub dx, ax ; undo fix-up for data segment in RAM
  6826.  
  6827. ; Temporarily use dword at DGROUP:0 to hold vector for jump.
  6828.  
  6829. mov ds, ax ; DGROUP
  6830. xor ax, ax
  6831. mov word ptr ds:[0], ax
  6832. mov word ptr ds:[2], dx ; computed segment for jump
  6833.  
  6834. jmp dword ptr ds:[0] ; to "startup"
  6835.  
  6836. ; and now back to your regularly scheduled data...
  6837.  
  6838. NULL ends
  6839.  
  6840. ; Variables declared as "const" will go here.
  6841.  
  6842. CONST segment word public 'CONST'
  6843. CONST ends
  6844.  
  6845. ; Initialized global and static variables will go here.
  6846.  
  6847. _DATA segment word public 'DATA'
  6848. public __acrtused, _errno
  6849. __acrtused dw 0 ; prevents linking in Microsoft's
  6850. _errno dw 0 ; standard start-up code &libs
  6851. _DATA ends
  6852.  
  6853. ; Uninitialized global and static variables will go here.
  6854. ; The C language guarantees that variables in this segment
  6855. ; will be initialized to 0 at load time. It is up to the
  6856. ; startup code to make good on this promise. Since we
  6857. ; are using exe2bin, it will fill in segments like this
  6858. ; one with zeros, but in many other environments, this
  6859. ; has to be done explicitly. If you change the build
  6860. ; process so that this becomes necessary, perhaps the
  6861. ; easiest way to deal with it is to just begin by filling
  6862. ; all of RAM with zeros. That way you'll also initialize
  6863. ; the FAR_BSS and HUGE_BSS at the same time, and you won't
  6864. ; have to figure out where any of them begin or end.
  6865.  
  6866. _BSS segment word public 'BSS'
  6867. _BSS ends
  6868.  
  6869. ; Note: exe2bin doesn't want to see a stack segment because
  6870.  
  6871. ; it knows the loader won't be able to set up the stack as
  6872. ; specified. This startup code will set up this stack for us,
  6873. ; but we need to trick exe2bin so that it won't know this is
  6874. ; a stack. So we define it as PUBLIC instead of STACK.
  6875.  
  6876. STACK segment word public 'STACK'
  6877. public __stackend
  6878. db STACKSIZE/8 dup ("<STACK> ")
  6879. __stackend label word
  6880. STACK ends
  6881.  
  6882. ;----------------------------------------------------------------------
  6883. ; data segments that are not part of dgroup
  6884. ;----------------------------------------------------------------------
  6885.  
  6886. FAR_DATA_START segment para public 'FAR_DATA'
  6887. FAR_DATA_START ENDS
  6888.  
  6889. FAR_BSS_START segment para public 'FAR_BSS'
  6890. FAR_BSS_START ends
  6891.  
  6892. HUGE_BSS_START segment para public 'HUGE_BSS'
  6893. HUGE_BSS_START ends
  6894.  
  6895. ;----------------------------------------------------------------------
  6896. ; mark the end of all the data in the image
  6897. ;----------------------------------------------------------------------
  6898.  
  6899. ENDDATAIMAGE segment para public 'ENDDATAIMAGE'
  6900. public __enddataimage
  6901. __enddataimage label byte
  6902. ENDDATAIMAGE ends
  6903.  
  6904. ;----------------------------------------------------------------------
  6905. ; Tell linker where to put the code segment.
  6906. ;----------------------------------------------------------------------
  6907.  
  6908. _TEXT segment para public 'CODE'
  6909. _TEXT ends
  6910.  
  6911. ;----------------------------------------------------------------------
  6912. ; mark the end of the entire image
  6913. ;----------------------------------------------------------------------
  6914.  
  6915. ENDIMAGE segment para public 'ENDIMAGE'
  6916. public __endimage
  6917. __endimage label byte
  6918. ENDIMAGE ends
  6919.  
  6920. ;======================================================================
  6921. ;
  6922. ; Start-up code
  6923. ;
  6924. ;----------------------------------------------------------------------
  6925.  
  6926. _TEXT segment
  6927. assume CS:_TEXT, DS:DGROUP, SS:DGROUP
  6928.  
  6929. extrn _main:near
  6930.  
  6931. public startup
  6932. startup:
  6933. ;----------------------------------------------------------
  6934. ; Initialize interrupt vectors to point to the restart vector.
  6935. ; This will cause a restart on any unexpected interrupt.
  6936. ;----------------------------------------------------------
  6937.  
  6938. xor ax, ax
  6939. mov ds, ax
  6940. xor bx, bx
  6941. mov cx, 256
  6942. init_int_loop:
  6943. mov [bx+0], 0FFF0h ; offset
  6944. mov [bx+2], 0F00h ; segment
  6945. add bx, 4
  6946. loop init_int_loop
  6947.  
  6948. ;----------------------------------------------------------
  6949. ; Set up a temporary stack
  6950. ;----------------------------------------------------------
  6951.  
  6952. mov ax, DGROUP
  6953. mov ds, ax
  6954. mov bx, offset DGROUP:__stackend
  6955. and bx, NOT 1 ; align sp on word boundary
  6956. mov ss, ax ; DGROUP
  6957. mov sp, bx ; __stackend
  6958.  
  6959. ;----------------------------------------------------------
  6960. ; Copy data image from ROM to RAM
  6961. ;
  6962. ; For later processors with protected mode, this code assumes
  6963. ; that the ROM is reflected down into the top of the first
  6964. ; 1MB of address space.
  6965. ;----------------------------------------------------------
  6966.  
  6967. mov ax, DGROUP
  6968. mov es, ax
  6969. mov dx, seg __enddataimage
  6970. sub dx, ax ; number of paragraphs to copy
  6971. mov ax, ROMSEG
  6972. mov ds, ax
  6973. copyloop:
  6974. xor si, si
  6975. xor di, di
  6976. mov cx, 8
  6977. rep movsw
  6978.  
  6979. mov ax, es ;\
  6980. inc ax ; > inc es
  6981. mov es, ax ;/
  6982.  
  6983. mov ax, ds ;\
  6984. inc ax ; > inc ds
  6985. mov ds, ax ;/
  6986.  
  6987. dec dx
  6988. jnz copyloop
  6989.  
  6990.  
  6991. ;----------------------------------------------------------
  6992. ; setup segment registers
  6993. ;----------------------------------------------------------
  6994.  
  6995. mov ax, DGROUP
  6996. mov ds, ax
  6997. mov es, ax
  6998.  
  6999. ;----------------------------------------------------------
  7000. ; setup C stack and first frame
  7001. ;----------------------------------------------------------
  7002.  
  7003. mov bx, offset DGROUP:__stackend
  7004. and bx, NOT 1 ; align sp on word boundary
  7005. mov ss, ax ; DGROUP
  7006. mov sp, bx ; __stackend
  7007. xor bp, bp ; frame 0
  7008. push bp, sp ; mark it on the stack
  7009. move bp, sp ; bp always has current frame pointer
  7010.  
  7011. ;----------------------------------------------------------
  7012. ; Call main()
  7013. ; note: CLI, CLD on entry.
  7014. ;----------------------------------------------------------
  7015.  
  7016. call _main
  7017.  
  7018. ;----------------------------------------------------------
  7019. ; For embedded apps, main() should never return.
  7020. ; If for some reason it does, we'll start all over
  7021. ;----------------------------------------------------------
  7022.  
  7023. ; (for some reason, it's difficult to convince
  7024. ; the assembler to generate this instruction.)
  7025. db 0EAh ; jmp far
  7026. dd 0F000FFF0h ; restart vector
  7027.  
  7028. _TEXT ends
  7029.  
  7030. ;=======================================================================
  7031. ;
  7032. ; Set entry point to beginning of image.
  7033. ; (This is done mostly to satisfy the assembler and exe2bin.)
  7034. ;
  7035. ------------------------------------------------------------------------
  7036. end jumpstart
  7037.  
  7038.  
  7039.  
  7040.  
  7041.  
  7042.  
  7043.  
  7044.  
  7045.  
  7046.  
  7047.  
  7048.  
  7049.  
  7050.  
  7051.  
  7052.  
  7053.  
  7054. Programming Flash Memory
  7055.  
  7056.  
  7057. Mike Cepek
  7058.  
  7059.  
  7060. Mike is a Senior Software Design Engineer in the Research and Development
  7061. department of Management Graphics, Inc., Minneapolis.Mike started programming
  7062. in the 7th grader, and later earned his B.S. in Computer Science at the
  7063. University of Minnesota/Institute of Technology in 1984. Mike can be reached
  7064. on the Internet at cepek@mgi.com.
  7065.  
  7066.  
  7067.  
  7068.  
  7069. Introduction
  7070.  
  7071.  
  7072. Flash memory is an increasingly popular choice over EPROMs for system
  7073. designers, especially for embedded systems. The devices available today allow
  7074. in-circuit reprogramming of megabytes of "ROM" with few or no additional
  7075. parts. This article describes the additional software required to program
  7076. these devices.
  7077.  
  7078.  
  7079. Why We Like Flash
  7080.  
  7081.  
  7082. One major benefit from flash is faster software development turnaround time.
  7083. Burning and installing EPROMs for each new version of firmware slows down
  7084. development. While downloading the firmware to RAM can eliminate these steps,
  7085. it assumes that we have adequate (i.e. more) RAM for the task, which is not
  7086. always a good assumption. This is because in an embedded system, code and
  7087. static data usually reside in ROM. Note also that the ROM and RAM versions of
  7088. the code are not identical -- the ROM and RAM address spaces will be
  7089. different, leading to different firmware relocation addresses. Using flash
  7090. memory avoids all these development issues.
  7091. Our embedded systems currently use two types of flash memory devices made by
  7092. AMD: the Am29F010 and Am29F040. These devices are single-voltage (5v),
  7093. byte-wide memories with 128 KB and 512 KB capacity, respectively. High storage
  7094. capacity is just one of the many device features available in flash memories
  7095. (see sidebar). For example, both of these devices divide their memory into
  7096. eight equal size sectors, each of which can be independently erased and
  7097. reprogrammed.
  7098. These two flash devices are compatible with the JEDEC pinout standard for
  7099. EPROM devices. This allows us to use flash during the development process and
  7100. to use off-the-shelf EPROMs in production where desired.
  7101. Our customers also appreciate flash because it enables them to install
  7102. firmware updates without disassembling the product to replace EPROMs. We have
  7103. even provided "media-less" firmware updates to customers via e-mail, which
  7104. they can download to the product and save in the flash memory.
  7105. [1] and [2] discuss other applications for flash, and discuss common problems
  7106. with and solutions for embedded system flash implementations.
  7107.  
  7108.  
  7109. Implementation Background
  7110.  
  7111.  
  7112. Code written for embedded systems is often highly specific to one platform.
  7113. However, by applying principles of generality and modularization, I believe we
  7114. have produced code that is reasonably portable and adaptable.
  7115. Our implementation uses two byte-wide flash chips in parallel, allowing direct
  7116. 16-bit reads and writes with minimum access time (see Figure 1). This leads to
  7117. some interesting program timing constraints, to be covered later in this
  7118. article.
  7119. The remainder of this article discusses the production code we use with these
  7120. devices. I describe the data structures defined in the file fmutl.h first, and
  7121. follow with a bottom-up presentation of the routines, defined in fmutl.c.
  7122. (These two source files are available on this month's code disk. The disk
  7123. includes another source module which uses these routines for diagnostic and
  7124. unit testing.)
  7125.  
  7126.  
  7127. Data Structures
  7128.  
  7129.  
  7130. Listing 1 shows the header file, fmutl.h. It contains the following:
  7131. typedefs for the FMINFO and FMERR structures
  7132. an extern for an FMERR instance
  7133. prototypes for the global routines in fmutl.c
  7134. The FMINFO structure holds information describing the characteristics of the
  7135. flash devices. This data allows the code to adapt to different flash devices
  7136. still having similar overall characteristics (e.g., programming algorithm).
  7137. The fm_status routine fills in the FMINFO member data.
  7138. The FMERR structure provides error information to the calling routine. A
  7139. global instance of this structure is defined in the fmutl. c module, and
  7140. fMerr. h provides an extern declaration.
  7141. Listing 2 shows the manifest constants and static variable definitions for the
  7142. program. (On the code disk, Listing 2 through Listing 7 comprise fmutl. c.)
  7143. Because our code accesses two byte-wide devices in parallel, we've duplicated
  7144. the command code bytes in the command constants -- the commands will be
  7145. written to, and data will be read back from, both devices simultaneously.
  7146. The constants FM_DATA1 and FM_DATA2 are offsets from the beginning (base) of
  7147. the flash memory space, and will be resolved to actual system addresses in the
  7148. flash memory address space at run time. The global definition of the fm_err
  7149. structure allows fmutl. c to export error information to calling routines.
  7150. DevTable is an array of FMINFO structures that describes the flash devices
  7151. supported by the program. The code uses this data at runtime to accommodate a
  7152. variety of devices. (One of our product models actually does use two sizes of
  7153. flash devices in the same unit.)
  7154.  
  7155.  
  7156. Flash Programming Routines
  7157.  
  7158.  
  7159. The routines in fmutl.c are ordered in traditional bottom-up-first fashion.
  7160. Accordingly, I begin my discussion with the low-level routines, followed by
  7161. mid- and high-level routines.
  7162. The fm_error routine (Listing 3) needs little explanation. It simply stores
  7163. its parameters in the global fm_err structure. We make this routine global to
  7164. allow calling modules to handle flash-related errors without duplication of
  7165. code. For example, a diagnostic test verifying data written to the flash could
  7166. handle a verification error by calling fm_error with its own unique error
  7167. code.
  7168.  
  7169.  
  7170.  
  7171. Picking the lock
  7172.  
  7173.  
  7174. In normal operation, flash memory behaves like ROM: values may be read from
  7175. ROM but writes are ignored. Attempts to write to ROM are usually programming
  7176. errors, and are thus rare. Flash devices take advantage of this rarity by
  7177. allowing for a series of special writes to act as "key." When a flash device
  7178. recognizes the predetermined sequence, it becomes available for erasing,
  7179. writing, and other operations.
  7180. The fm_cmd routine (Listing 3) recites the magic incantation to unlock the
  7181. devices into the special mode selected by the third parameter. If any of the
  7182. writes contain unexpected data, the devices revert to read-only mode.
  7183. We use the static variable pBase to simplify parameter passing. pBase is
  7184. initialized by the two global routines fm_status and fm_write, to point to the
  7185. base address of the flash memory space. (In our products with two sets of
  7186. flash devices, one set starts at system address 0x00000000, the other at
  7187. 0x64000000.)
  7188. Because flash memory is implemented in the system as normal read/write memory,
  7189. there are no special timing considerations during normal operation. We can use
  7190. normal memory read and write accesses from high-level code to perform all the
  7191. special device functions.
  7192.  
  7193.  
  7194. Mid-Level Routines
  7195.  
  7196.  
  7197. Listing 4 shows the routines fm_protected and fm_status. The former uses a
  7198. special mode of the devices to query if any of the memory sectors are
  7199. protected. Protected sectors cannot be erased or reprogrammed, and typically
  7200. store bootstrap or BIOS type code. (We could have implemented the fields in
  7201. FMINFO for reporting protected sector information as bits within a word or a
  7202. long, but this would place an artificial limit on the number of sectors in a
  7203. device. Some flash devices have as many as 1024 sectors.)
  7204. FMINFO represents the protected sectors using a start sector and number of
  7205. sectors. This method assumes that the case of non-contiguous protected sectors
  7206. is not interesting, since bootstrap and BIOS code is usually in a single area
  7207. located at the highest or lowest area of memory. We use the shift-right
  7208. operator, as found in the first two lines, throughout this module to convert
  7209. byte lengths to word lengths, since all pointer arithmetic is word-based.
  7210. To sense which sectors are protected, fm_protected calls fm_cmd to place the
  7211. devices in AutoSelect mode. In this mode, reads from certain offsets in the
  7212. flash address space will return chip status information instead of real data.
  7213. The constants with the FM_AO_prefix identify these special address offsets.
  7214. Inside the loop, the routine reads a word from each sector. The
  7215. FM_PROT_SECT_MASk bits within the word indicate whether that sector is
  7216. protected in either device.
  7217. The fm_status routine is global. It uses AutoSelect mode to read back ID bytes
  7218. indicating the device manufacturer and the device type. The routine scans the
  7219. DevTable array of known device information for a match, and initializes the
  7220. static FMInfo structure with device-specific information. fm_status returns a
  7221. non-zero value if the devices are known, which can be used as a quick check
  7222. for functioning flash.
  7223. Also, fm_status can optionally copy the FMInfo static structure to a structure
  7224. designated by the caller. Diagnostics, for example, could display the pDevName
  7225. string to confirm the type of devices installed.
  7226.  
  7227.  
  7228. Erasing Sectors
  7229.  
  7230.  
  7231. fm_sector_erase (the second routine in Listing 5) uses the Sector Erase
  7232. feature of the devices to prepare them for being written with new data. Sector
  7233. Erase erases all locations within a sector at once, which is where the term
  7234. "flash" comes from. Conventional EEPROM technology erases each location
  7235. separately. Any number of flash sectors can be erased concurrently.
  7236. One curious thing about flash is that this "electrically erased" condition
  7237. results in all bits being set to 1. A related curiosity is that 1 bits erase
  7238. faster than 0 bits. (See [7] for a good explanation of how flash memory
  7239. operates at the cell level.) The time required for erasure also varies with
  7240. device temperature and the number of lifetime erase/write cycles performed.
  7241. Figure 2 shows how erase and write times increase with use for an Am29F010.
  7242. See also [8] for more information on erasure time.
  7243. In anticipation of success -- where fm_error is not called -- fm_sector_erase
  7244. and fm_write both clear the fm_err structure to all zeros. This action
  7245. prevents uninitialized values from being left for the caller in fm_err. Note
  7246. that fm_err cannot be initialized in its declaration, since embedded system
  7247. compilers tend to place initialized data into ROM rather than RAM.
  7248. The construction and use of the SectorAddrMask variable deserves mention. The
  7249. code uses this variable to convert a pointer into a flash sector to a byte
  7250. offset from the base of that sector. This conversion assumes that the base
  7251. address of the flash (FBASE) in the system address space is aligned to an even
  7252. multiple of the flash address range (FSIZE) -- i.e., FBASE mood FSIZE must be
  7253. zero. This assumption is significant when supporting devices of various sizes.
  7254. Prior to the first loop in fm_sector_erase, the devices are unlocked for
  7255. erasure. The first loop completes the sector erasure command sequence by
  7256. writing a secondary command code to an erase within each sector to be erased.
  7257. The devices consider the erase command sequence complete if they receive no
  7258. secondary command within a certain time period (e.g. 80 msec), so it may be
  7259. necessary to disable interrupt service routines during this time.
  7260. The second loop in fm_sector_erase waits for the erase operation to complete.
  7261. The erase and write processes are controlled by an algorithm internal to the
  7262. devices. When these internal algorithms are running, reading from the devices
  7263. returns status information instead of ROM data. In particular, the high bit of
  7264. the byte is inverted from the final, expected value. This operation guarantees
  7265. that when the expected value occurs, the erase or write operation has
  7266. completed successfully, which leads to a very simple polling loop for success.
  7267. The erase is complete when the readback of any erased location returns
  7268. FM_ERASE_VAL.
  7269.  
  7270.  
  7271. Device Time-outs
  7272.  
  7273.  
  7274. The fail-safe loop in fm_sector_erase ensures that malfunctioning hardware
  7275. does not cause the firmware to hang. An earlier version of this routine
  7276. assumed that completion or time-out were the only two possible outcomes.
  7277. However, the specific tests used for completion and time-out ignore many other
  7278. possible bit combinations which, though rare, should be tolerated by robust
  7279. code.
  7280. A "feature" of that earlier code version was the lack of an
  7281. implementation-dependent timing constant like E_FS_ITER. Using such a constant
  7282. may not be desirable, but I don't know of a way to handle all three cases
  7283. (success, time-out, and other) without assuming another
  7284. implementation-dependent resource, such as a watchdog timer.
  7285. fm_sector_erase calls the fm_tofs_error routine (Listing 5) if the fail-safe
  7286. loop expires. Its job is to determine whether or not a timeout occurred. This
  7287. process sounds simple enough, but two things complicate the code here: timeout
  7288. detection itself and parallel devices.
  7289. Figure 3 illustrates the byte data returned by polling an erased location
  7290. during an erase cycle. While the erase is in progress, bit 7 (DQ7) shows the
  7291. inverse of the final expected data. If a location cannot be successfully
  7292. erased, bit 5 (DQ5) will go high when the device gives up trying and times
  7293. out. The data book warns that DQ5 and DQ7 may change at the same time. Thus, a
  7294. single read showing DQ5 high cannot be trusted as indicating a timeout, since
  7295. DQ7 may be transitioning to final valid data, which might coincidentally have
  7296. DQ5 set.
  7297. A further complication is that we are using two devices in parallel. The case
  7298. where only one device times out must be properly handled. I will leave as an
  7299. exercise for the reader to figure out the nuances of Boolean logic in the
  7300. first three lines of code.
  7301.  
  7302.  
  7303. Programming New Data
  7304.  
  7305.  
  7306. Listing 6 shows the fm_cpy routine. fm_cpy writes new data into sectors which
  7307. are assumed to have been erased. The data books refer to this operation as
  7308. "programming" the data. (The parameter order purposely mimics memcpy.)
  7309. Each byte to be written requires a separate unlock sequence. We applied heavy
  7310. optimization techniques to this routine to minimize the effect of this
  7311. overhead. Our hand-optimization (mentioned in the function header comment) is
  7312. aware of the kind of assembly code that will ultimately be generated here. For
  7313. example, using a short instead of an int for the fail-safe counter allowed the
  7314. compiler to generate a faster looping construct. An 11 percent improvement in
  7315. speed resulted from this one change. This routine is then both optimal for our
  7316. specific implementation and portable.
  7317. The bulk of this routine will look familiar from previous routines. For
  7318. efficiency, we replicate a call to fm_cmd using register pointers and data. We
  7319. then write the bytes to be programmed to the desired location. The completion
  7320. polling loop and error handling mimics the code in fm_sector_erase.
  7321. As indicated by a comment statement, two program lines occuring before the
  7322. inner loop perform a kind of parallel processing. After we write new data to
  7323. the current location we must wait for the write to complete. We can execute
  7324. more program statements in the meantime. Analysis shows that these two lines
  7325. execute well before the write completes. This results in fewer polling
  7326. iterations, which speeds up the innermost loop.
  7327.  
  7328.  
  7329. The High-Level Routine
  7330.  
  7331.  
  7332. The fm_write routine (Listing 7) makes use of all the other routines in
  7333. fmutl.c to accomplish its function: writing an arbitrary sized buffer of data
  7334. to an arbitrary area of the flash.
  7335. We chose to abstract to this level for a number of reasons. Writing new data
  7336. to all of flash, our most common operation, requires a simple call:
  7337. fm_write(FLASH_BASE, FLASH_BASE, pNewData, DataSize, 0);
  7338. The complexities of sector sizes, erasing before writing, device
  7339. identification, and so on are encapsulated.
  7340. Because sectored flash devices can only erase entire sectors, writes that are
  7341. not aligned to sector boundaries require special handling. Rather than
  7342. implement this complexity in various separate locations, we chose to
  7343. encapsulate it as well.
  7344.  
  7345.  
  7346.  
  7347. Optional Scratch Buffer
  7348.  
  7349.  
  7350. The encapsulation approaches presented thus far cannot hide one aspect of
  7351. sectored flash memory which differs from normal RAM: It's impossible to change
  7352. just one byte within a sector.
  7353. Consider the case where a program calls fm_write to write a single word to a
  7354. sector already containing healthy data. Erasing the sector and writing the new
  7355. word leaves the remaining words in that sector changed to 0xFFFF -- not likely
  7356. what the caller expected.
  7357. To get around this problem, the pScrBuf parameter lets the caller pass a
  7358. pointer to a buffer. If pScrBuf is not a NULL, uses it to save and restore any
  7359. existing flash sector data not being reprogrammed.
  7360. If pScrBuf is NULL, fm_write transfers the caller's new data directly to the
  7361. flash sector. Non-buffered transfers accommodate the case in which the caller
  7362. wishes to transfer a large block of data to flash memory and doesn't care what
  7363. happens to the remaining (unwritten) portion of the sector. Giving the caller
  7364. this control is a performance feature, since not using the buffer saves time.
  7365. Allowing the caller to provide the buffer also avoids forcing fm_write to
  7366. acquire an implementation-dependent resource (memory) at run time. The caller
  7367. can use fm_status and the SectorSize field of the FMINFO structure to size
  7368. this buffer.
  7369.  
  7370.  
  7371. The fm_write algorithm
  7372.  
  7373.  
  7374. The function header comments describe the external interface. The following
  7375. pseudo-code provides an overview of how the routine works:
  7376. fm_status();
  7377. if (bad parameters)
  7378. fm_error();
  7379. if (not starting at a sector boundary)
  7380. handle partial write to first sector;
  7381. if (data doesn't end on a sector boundary)
  7382. setup for partial last sector write;
  7383. erase the remaining sectors;
  7384. write the remaining user data;
  7385. if (last sector was partial && pScrBuf)
  7386. write pScrBuf data to last sector;
  7387. The bulk of this routine handles the partial sector cases. If the caller only
  7388. writes complete sectors, the extra complexity isn't used.
  7389. The first major conditional, based on keep_before, handles the case where the
  7390. beginning of the write doesn't start on an even sector boundary. In this case,
  7391. the algorithm computes the following key length values:
  7392. keep_before = number of bytes before user data
  7393. wri_len = number of bytes of user data
  7394. keep_after = number of bytes after user data
  7395. These values reflect the start and end boundaries of the user data within the
  7396. initial sector. If a scratch buffer was provided, the buffer is used to save
  7397. the original contents of the sector. Next, the routine erases the sector and
  7398. writes the new user data. Then, the original sector data before and/or after
  7399. the new user data is restored, if a scratch buffer was provided.
  7400. The next major conditional, based on wri_len, handles the case where the write
  7401. does not end on an even sector boundary. The wri_len and keep_after variables
  7402. are recomputed. If a buffer was provided it is loaded with the original
  7403. contents of the last sector.
  7404. Next, the routine erases the remaining sectors and writes them with the new
  7405. user data. Finally, the routine writes the remainder of the final sector from
  7406. the scratch buffer, if provided.
  7407.  
  7408.  
  7409. Testing
  7410.  
  7411.  
  7412. The code disk contains a third source module, flashtst.c, that we have used to
  7413. unit test these routines. One routine performs a few basic memory tests
  7414. (zeros, ones, unique data) suitable for a manufacturing check-out process.
  7415. Another routine performs an exhaustive test of all combinations of writes on
  7416. and off sector boundaries, which helps test the code integrity.
  7417. This module can be compiled as a stand-alone utility or as an independently
  7418. linkable module. While modularized, it does rely on external routines
  7419. available in our embedded system for user input and output.
  7420.  
  7421.  
  7422. The Tip of the Iceberg
  7423.  
  7424.  
  7425. The code presented here works well for our purposes, but there are many extra
  7426. features supported by the devices that you may want to use:
  7427. Many flash devices support an erase suspend feature. Recall that during erase,
  7428. data reads return status information rather than real data. With some hardware
  7429. assistance an application could implement "background erasing." By using
  7430. interrupt handlers to suspend and resume erasing, data could be read from
  7431. sectors not being erased, which would allow the application to continue while
  7432. the erase occured during idle bus cycles.
  7433. Creative algorithms could also take advantage of the fact that flash writes
  7434. can change 1-bits to 0-bits (but not vice-versa) without an erase cycle.
  7435. "Wear leveling" is a technique commonly applied to heavy use flash
  7436. implementations. You can maximize device lifetime with low-level remapping of
  7437. the sectors as necessary to keep them all at roughly the same number of erase
  7438. and write cycles. You can also automatically "retire" sectors as they become
  7439. unusable.
  7440. File systems are a typical next step beyond using flash as a better EPROM.
  7441. There are various hardware and software solutions available today implementing
  7442. this logical next step.
  7443.  
  7444.  
  7445. Source Sources
  7446.  
  7447.  
  7448. Source code for interfacing with flash devices is available. Intel provides
  7449. assembly and C code in their flash data books (see [6]). AMD offers Embedded
  7450. Development Kit Driver Software free for the asking or via their California
  7451. BBS (contact your local rep).
  7452.  
  7453.  
  7454. Conclusion
  7455.  
  7456.  
  7457.  
  7458. Flash memory allows embedded systems to easily adapt to changing requirements.
  7459. After working with flash memory devices for awhile now in a fast-paced
  7460. development environment, I can't imagine going back to burning EPROMs. And the
  7461. features flash provides to our customers make our products even more
  7462. competitive.
  7463. References
  7464. [1] Arvind Rana. "Designing and Debugging with Flash ROMs," Proceedings of the
  7465. Sixth Annual Embedded Systems Conference, Volume 2, September 1994, pp.
  7466. 145-153.
  7467. [2] Brian Dipert and Markus Levy, Designing With Flash Memory (Annabooks,
  7468. 1994).
  7469. [3] Advanced Micro Devices, Inc. 1994/1995 Flash Memory Products Data
  7470. Book/Handbook.
  7471. [4] Atmel Corporation 1994 Single Voltage Flash Memory Design & Application
  7472. Book.
  7473. [5] Intel 1994 Flash Memory data books Volumes I & II.
  7474. [6] ibid., Vol. I, Chapters 3 and 4, Application Notes.
  7475. [7] ibid., Vol. II, ppg 9-1 to 9-5, Engineering Report ER-20.
  7476. [8] ibid., Vol. II, pg 9-11, Figure 5.
  7477. Flavors of Flash
  7478. Manufacturers like Intel, AMD, Atmel and others offer a variety of flash
  7479. memory devices with a long list of features.
  7480. Here is a partial list:
  7481. 32 KB to 4 MB capacity per device (and growing)
  7482. 1,000 to 100,000 minimum erase/write cycles
  7483. 8 to 1,024 sectors
  7484. 45 to 250 nsec access time
  7485. same size sectors versus "Boot-block" sizes
  7486. byte versus word accessing (some pin-selectable)
  7487. pin-out upgradable to larger capacity (some models)
  7488. JEDEC pin-out and command compatible (some models)
  7489. 3.3 versus 5.0 volt (some pin-selectable)
  7490. 12 volts erase/write voltage (some models)
  7491. power-down modes (some models)
  7492. on-board RAM buffering for writes (some models)
  7493. writes during erase (some models)
  7494. PCMCIA flash memory cards (e.g. Intel's 40 MB Memory Card) utilize an array of
  7495. flash chip dies within a single package.
  7496. Figure 1 Two flash devices wired in parallel
  7497. Figure 2 Erase and write times vs. device cycles
  7498. Figure 3 Simple timing diagram of four signals over time
  7499.  
  7500. Listing 1 Data structures used by flash memory programmer
  7501. /* fmutl.h - flash memory utility module header */
  7502.  
  7503. #ifndef FMUTL_H_____LINEEND____
  7504. #define FMUTL_H_____LINEEND____
  7505.  
  7506. /* macro returning number of elements in an array: */
  7507. #define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
  7508.  
  7509. /***********************************************************************
  7510. The fm_status() routine can return the following information about flash
  7511. memory devices. NOTE: fields ending with '_' are intended for internal
  7512. use by fmutl.c only:
  7513. ***********************************************************************/
  7514.  
  7515. typedef struct { /** Flash Memory information structure: **/
  7516. /* private: */
  7517. unsigned short MfrID_; /* (manufacturer ID code) */
  7518. unsigned short DevID_; /* (device ID code) */
  7519. short MaxErase_; /* (max erase time in seconds) */
  7520. short MaxWrite_; /* (max byte-write time in ms) */
  7521. int NumSect_; /* (number of sectors per chip) */
  7522. /* public: */
  7523. size_t SectorSize; /* bytes per sector (both chips) */
  7524. size_t TotalSize; /* total number of bytes (both chips) */
  7525. int NumProt; /* >= O: # of protected sectors;
  7526.  
  7527. -1: prot. sectors are non-contiguous
  7528. -2: prot. mismatched b/w devices */
  7529. int FirstProt; /* first protected sector, ordinal 0 */
  7530. char *pDevName; /* ptr to manufacturer & device name */
  7531. } FMINFO;
  7532.  
  7533. /***********************************************************************
  7534. If the return value of a routine in fmutl.c indicates failure, the fm_err
  7535. global structure will contain the following additional information about
  7536. the error.
  7537. ***********************************************************************/
  7538.  
  7539. typedef struct { /** Flash routine error information structure: **/
  7540. unsigned short *addr; /* flash memory address of problem */
  7541. unsigned short exp; /* value expected */
  7542. unsigned short act; /* actual value read */
  7543. unsigned char code; /* error code byte (see fmutl.c) */
  7544. char *pMsg; /* ptr to error message string */
  7545. } FMERR;
  7546.  
  7547. extern FMERR fm_err; /* defined in fmutl.c */
  7548.  
  7549. /**********************************************************************
  7550. Prototypes for the global routines in fmutl.c:
  7551. ***********************************************************************/
  7552.  
  7553. #ifdef__STDC______LINEEND____
  7554.  
  7555. extern fm_status(unsigned short *pBase, FMINFO *pFMIfo);
  7556.  
  7557. extern fm_write(unsigned short *pBase, unsigned short *pDst,
  7558. unsigned short *pSrc, size_t length,
  7559. unsigned short *pScrBuf);
  7560.  
  7561. extern void fm_error(unsigned short *addr, unsigned short exp,
  7562. unsigned short act, int errcode);
  7563.  
  7564. #endif
  7565.  
  7566. #endif /* FMUTL_H_ */
  7567.  
  7568. /* End of File */
  7569.  
  7570.  
  7571. Listing 2 Constants and Statics
  7572. #include <string.h>
  7573. #include "fmutl.h"
  7574.  
  7575. /** all these magic numbers are from the AMD spec sheets: **/
  7576.  
  7577. /* address and data for command writes: */
  7578. #define FM_ADDR1 0x5555 /* these are for the first "magic */
  7579. #define FM_DATA1 0xAAAA /* write" of a command preamble */
  7580. #define FM_ADDR2 0x2AAA /* these are for the second "magic */
  7581. #define FM_DATA2 0x5555 /* write" of a command preamble */
  7582.  
  7583. /* command codes: */
  7584. #define FMCMD_RESET 0xF0F0 /* "Read/Reset Command" */
  7585. #define FMCMD_AUTOSEL 0x9090 /* "Autoselect Command" */
  7586.  
  7587. #define FMCMD_WRITE 0xA0A0 /* "Byte Program Command" */
  7588. #define FMCMD_ERASE 0x8080 /* "Chip or Sector Erase Command" */
  7589.  
  7590. /* use this to skip last step of a command preamble [see fm_cmd()]: */
  7591. #define FMCMD_NONE 0x0000 /* no command */
  7592.  
  7593. /* secondary command codes for erase commands: */
  7594. #define FMCMD2_SECTOR 0x3030 /* "Sector Erase" */
  7595. #define FMCMD2_CHIP 0x1010 /* "Chip Erase" */
  7596.  
  7597. /* miscellaneous other flash memory constants: */
  7598. #define FM_PROT_SECT_MASK 0x0101 /* protected sector indicator bit(s) */
  7599. #define FM_ERASE_VAL 0xFFFF /* value of an erased location */
  7600. #define FM_TIMEOUT 0x2020 /* DQ5 - goes hi if device times out */
  7601. #define FM_NDATA_POLL 0x8080 /* DQ7 - inverse of final data */
  7602.  
  7603. /* special address offsets for Autoselect command reads: */
  7604. #define FM_AO_MANUF 0 /* for manufacturer code readback */
  7605. #define FM_AO_DEVID 1 /* for device ID readback */
  7606. #define FM_AO_PROT_SECT 2 /* for protected sector readbacks */
  7607.  
  7608. FMERR fm_err; /* global Flash error structure */
  7609.  
  7610. static char *ErrMsgs[] = [ /** fm_err.code and fm_err.pMsg values: **/
  7611. "x?? Unknown err", /* 0:. unused */
  7612. "Bad devID codes", /* 1: unknown/mismatched/missing/bad chips */
  7613. "Erase fail-safe". /* 2: sector erase fail-safe triggered */
  7614. "Erase timed out", /* 3: sector erase command timed out */
  7615. "Write fail-safe", /* 4: byte write fail-safe triggered */
  7616. "Write timed out", /* 5: byte write programming timed out */
  7617. "fm_write params", /* 6: address or length parameter was odd */
  7618. "fm_write overfl", /* 7: attempt to write past end of flash */
  7619. }; /** see also ErrMsg[] in fm_error()**/
  7620.  
  7621. static FMINFO DevTable[] = { /* known device data table: */
  7622. /* MfrID DevID EFS WFS NS SectSiz*2 TotSize*2 NP FP DevNameString */
  7623. 0x0101, 0x2020, 20, 90, 8, 0x04000*2, 0x20000*2, 0, 0, "AMD Am29F010",
  7624. 0x0101, 0xA4A4, 40, 90, 8, 0x10000*2, 0x80000*'2, 0, O, "AMD Am29F04*",
  7625. };
  7626.  
  7627. /* the routines in this module need to share this data: */
  7628. static unsigned volatile short *pBase; /* ptr to the base address of Flash */
  7629. static FMINFO FMInfo; /* info specific to these chips */
  7630.  
  7631. /* End of File */
  7632.  
  7633.  
  7634. Listing 3 Low-level routines
  7635. /****************************************************************
  7636. Set values in the global error structure.
  7637. ****************************************************************/
  7638.  
  7639. void fm_error(addr, exp, act, errcode)
  7640. unsigned short *addr, exp, act;
  7641. int errcode; /* only the low byte is significant */
  7642. {
  7643. static char ErrMsg[20];
  7644. static char Hex[] = "0123456789ABCDEF";
  7645.  
  7646.  
  7647. /* fill in the easy stuff: */
  7648. fm_err.addr = addr;
  7649. fm_err.exp = exp;
  7650. fm_err.act = act;
  7651. fm_err.code = (unsigned char) errcode;
  7652.  
  7653. /* either use a built-in message, or construct one using errcode: */
  7654. if (1 <= errcode && errcode < ARRAY_LEN(ErrMsgs))
  7655. fm_err.pMsg = ErrMsgs[errcode];
  7656. else
  7657. { strncpy(ErrMsg, ErrMsgs[O], sizeof(ErrMsg));
  7658. ErrMsg[1] = Hex[fm_err.code / 16];
  7659. ErrMsg[2] = Hex[fm_err.code % 16];
  7660. fm_err.pMsg: ErrMsg;
  7661. }
  7662.  
  7663. }
  7664.  
  7665. /**********************************************************************
  7666. Send unlock commands to the chips. This magic permutation of writes takes
  7667. the chips out of normal read-only mode, and puts them into the indicated
  7668. command mode (see the FMCMD_constants). This code is replicated in the
  7669. fm_cpy() routine for performance reasons.
  7670. ***********************************************************************/
  7671.  
  7672. static void fm_cmd(cmd)
  7673. unsigned short cmd; /* command code = an FMCMD_ constant */
  7674. {
  7675. /* write first magic value to first magic location: */
  7676. *(pBase + FM_ADDR1) = FM_DATA1;
  7677.  
  7678. /* write second magic value to second magic location: */
  7679. *(pBase + FM_ADDR2) = FM_DATA2;
  7680.  
  7681. /* if desired, write a command code to the final magic location: */
  7682. if (cmd != FMCMD_NONE)
  7683. *(pBase + FM_ADDR1) = cmd:
  7684. }
  7685.  
  7686. /***********************************************************************
  7687. Reset the devices. Two resets are performed because the first may not
  7688. be accepted if the devices were left in a strange state (e.g. if a prior
  7689. command sequence was interrupted before completion). The second is sure
  7690. to be heard by sane devices.
  7691. ***********************************************************************/
  7692.  
  7693. static void fm_reset()
  7694. {
  7695. fm_cmd(FMCMD_RESET);
  7696. fm_cmd(FMCMD_RESET):
  7697. }
  7698. /* End of File */
  7699.  
  7700.  
  7701. Listing 4 Status Routines
  7702. /***********************************************************************
  7703. Provides information about protected sectors in the chips. FMInfo and pBase
  7704. are assumed to be initialized. Leaves the chips in AutoSelect mode.
  7705. ***********************************************************************/
  7706.  
  7707.  
  7708. static void fm_protected(pFirstProt, pNumProt)
  7709. int *pFirstProt, *pNumProt; /* see the FMINFO struct for details */
  7710.  
  7711. {
  7712. unsigned volatile short *ptr; /* ptr into flash space */
  7713. unsigned short val; /* value read back */
  7714. int sect; /* iterates thru the sectors */
  7715.  
  7716. /* init our pointer to the magic offset in the last sector: */
  7717. ptr = pBase + ((FMInfo. TotalSize >> 1) + FM_AO_PROT_SECT);
  7718. ptr -= FMInfo.SectorSize >> 1;
  7719.  
  7720. /* command the chip to read back the protected sector data: */
  7721. fm_cmd(FMCMD_AUTOSEL);
  7722.  
  7723. /* initialize these to mean "no protected sectors found": */
  7724. *pNumProt = 0;
  7725. *pFirstProt = -1;
  7726.  
  7727. /* loop checking each sector: */
  7728. for (sect = FMInfo.NumSect; --sect >= 0; )
  7729. {
  7730.  
  7731. /* bit set indicates sector is protected: */
  7732. val = *ptr & FM_PROT_SECT_MASK;
  7733. if (val)
  7734. { /* if both devices don't agree, error out: */
  7735. if (val!= FM_PROT_SECT_MASK)
  7736. { *pNumProt = -2;
  7737. return;
  7738.  
  7739. }
  7740.  
  7741. /* if protection is non-continuous error out: */
  7742.  
  7743. if (*pFirstProt != -1 && *pFirstProt != sect + 1)
  7744. { *pNumProt = -1;
  7745. return;
  7746. }
  7747.  
  7748. /* set index to this sector and bump the count: */
  7749. *pFirstProt = sect;
  7750. ++*pNumProt;
  7751. }
  7752.  
  7753. /* bump pointer to the magic location in the next sector: */
  7754. ptr -= FMInfo.SectorSize >> 1;
  7755. }
  7756. }
  7757.  
  7758. /*********************************************************************
  7759. Resets and returns data about the flash chips. Fills in our local
  7760. FMInfo structure, and optionally fills in an FMINFO structure for the
  7761. caller. Leaves the chip(s) in the normal ROM state. Zero is returned
  7762. on error and fm_err contains failure data.
  7763. *********************************************************************/
  7764.  
  7765. fm_status(pbase, pFMInfo)
  7766.  
  7767. unsigned short *pbase; /* must point to the *base* of the Flash memory */
  7768. FMINFO *pFMInfo; /* may be NULL */
  7769. {
  7770. unsigned short mfr, dev; /* IDs read back from devices */
  7771. FMINFO *pDev; /* traverses the DevTable[] */
  7772.  
  7773. /* share this with other routines in this module: */
  7774. pBase = pbase;
  7775.  
  7776. /* clear out error structure: */
  7777. memset(&fm_err, 0, sizeof(fm_err));
  7778.  
  7779. /* reset the chip: */
  7780.  
  7781. fm_reset();
  7782.  
  7783. /* send down command to read back the embedded data: */
  7784. fm_cmd(FMCMD_AUTOSEL);
  7785.  
  7786. /* read back the codes: */
  7787. mfr = *(pBase + FM_AO_MANUF);
  7788. dev = *(pBase + FM_AO_DEVID);
  7789. /* check for known devices: */
  7790.  
  7791. pDev = DevTable + ARRAY_LEN(DevTable);
  7792. while (--pDev >= DevTable)
  7793. if (pDev->MfrID_== mfr && pDev->DevID_ == dev)
  7794. { memcpy(&FMInfo, pDev, sizeof(FMInfo));
  7795. break;
  7796. }
  7797.  
  7798. /* if not found in our table, return error: */
  7799. if (pDev < DevTable)
  7800. { /* (could also be mismatched, missing, or fried devices) */
  7801. fm_reset();
  7802. fm_error(pBase, mfr, dev, 1);
  7803. return 0;
  7804. }
  7805.  
  7806. /* go get & fill in protected sector information: */
  7807. fm_protected(&FMInfo.FirstProt, &FMInfo.NumProt);
  7808.  
  7809. /* fill in structure for caller if they want us to: */
  7810. if (pFMInfo)
  7811. memcpy(pFMInfo, &FMInfo, sizeof(FMINFO));
  7812.  
  7813. /* exit readback mode and we're done: */
  7814. fm_reset():
  7815. return 1;
  7816. }
  7817. /* End of File */
  7818.  
  7819.  
  7820. Listing 5 Time-out and erase routines
  7821. /***********************************************************************
  7822. Handle a time-out or fail-safe error. The expected value is checked with
  7823. the actual value to determine if a time-out happened. fm_error is called
  7824. with errcode for a fail-safe error, or with errcode+1 for a time-out error.
  7825. ***********************************************************************/
  7826.  
  7827.  
  7828. static void fm_tofs_error(addr, exp, act, errcode)
  7829. unsigned short *addr, exp, act;
  7830. int errcode; /* fail-safe error=errcode; time-not error=errcode+1 */
  7831. {
  7832. unsigned short to_val; /* value used to check for time-out */
  7833. unsigned short tmp; /* time-out check temporary */
  7834.  
  7835. /* this value has timeout & data-poll bits set to mean time-out: */
  7836. to_val = (~exp & FM_NDATA_POLL) FM_TIMEOUT;
  7837.  
  7838. /* mask out the interesting bits and xor them: */
  7839. tmp = (act & (FM_TIMEOUT FM_NDATA_POLL)) ^ to_val;
  7840.  
  7841. /* both bits clear in a byte means that chip timed-out: */
  7842. if (!(tmp & 0x00ff) !(tmp & 0xff00))
  7843. ++errcode;
  7844.  
  7845. /* timeout error or fail-safe error handling: */
  7846. fm_error(addr, exp, act, errcode);
  7847.  
  7848. /* leave chips usable */
  7849. fm_reset();
  7850. }
  7851.  
  7852. /*********************************************************************
  7853. Erase by sectors. The sector(s) which are overlapped by the indicated
  7854. flash address memory range are erased. Returns non-zero on success;
  7855. otherwise zero is returned and fm_err contains failure data. Assumes
  7856. FMInfo and pBase are initialized.
  7857. *********************************************************************/
  7858.  
  7859. #define E_FS_ITER 240000 /* number of fail-safe loop iterations per sec. */
  7860. /** Re-measure this constant if the fail-safe loop is changed! **/
  7861.  
  7862. static fm_sector_erase(pLow, pHigh)
  7863. volatile unsigned short *pLow; /* ptr somewhere into first sector to erase */
  7864. volatile unsigned short *pHigh; /* ptr somewhere into last sector to erase */
  7865. {
  7866. size_t SectorAddrMask; /* converts ptr to offset within a sector */
  7867. size_t SectOffset; /* offset into ending sector */
  7868. unsigned long fail_safe; /* safety counter */
  7869.  
  7870. /* this masks off address bits indicating offsets into a sector: */
  7871. SectorAddrMask = FMInfo.SectorSize - 1;
  7872.  
  7873. /* set end pointer to the last location in it's sector: */
  7874. SectOffset = ((size_t) pHigh) & SectorAddrMask;
  7875. pHigh = pHigh - (SectOffset >> 1) + (FMInfo.SectorSize >> 1) - 1;
  7876.  
  7877. /* send erase preamble sequence: */
  7878. fm_cmd(FMCMD_ERASE);
  7879.  
  7880. /* send sector erase preamble sequence: */
  7881. fm_cmd(FMCMD_NONE);
  7882.  
  7883. /* loop indicating which sectors to erase: */
  7884. while (pLow <= pHigh)
  7885. {
  7886.  
  7887. /* tell chip to erase this sector: */
  7888. *pLow = FMCMD2_SECTOR;
  7889.  
  7890. /* bump pointer by one sector: */
  7891. plow += FMInfo.SectorSize >> 1;
  7892. }
  7893.  
  7894. /* loop checking for erase completion: */
  7895. for (fail_safe = FMInfo.MaxErase_* E_FS_ITER; --fail_safe; )
  7896. if (*pHigh == FM_ERASE_VAL)
  7897. return 1; /* done! */
  7898.  
  7899. /* time-out and fail-safe error handling: */
  7900. fm_tofs_error(pHigh, FM_ERASE_VAL, *pHigh, 2);
  7901. return 0;
  7902. }
  7903.  
  7904. /* End of File */
  7905.  
  7906. Listing 6 Data copy routine
  7907. /***********************************************************************
  7908. Internal routine to copy data to Flash Memory. It is assumed that the
  7909. destination area has been erased, and that bytes is an even number.
  7910. Returns non-zero on success; zero is returned on error and fm_err contains
  7911. failure data. FMInfo and pBase are assumed initialized.
  7912. * * * NOTE: This routine has been hand optimized for performance. * * *
  7913. ***********************************************************************/
  7914.  
  7915. #define W_FS_ITER 350 /* number of fail-safe loop iterations per msec */
  7916. /** Re-measure this constant if the fail-safe loop is changed! **/
  7917.  
  7918. static fm_cpy(pDst, pSrc, bytes)
  7919. register volatile unsigned short *pDst; /* where in Flash to copy data to */
  7920. register unsigned short *pSrc; /* where to copy data from */
  7921. register size_t bytes; /* # bytes to copy -- must be even */
  7922. {
  7923. register volatile unsigned short *ptr1; /* fast FM_ADDR1 ptr */
  7924. register volatile unsigned short *ptr2; /* fast FM_ADDR2 ptr */
  7925. register unsigned short fmd1, fmd2; /* fast FM_DATA values */
  7926. register unsigned short val; /* value read in verify loop */
  7927. register unsigned short fail_safe; /* safety counter */
  7928.  
  7929. /* put these in registers for speed: */
  7930. ptr1 = pBase + FM_ADDR1;
  7931. ptr2 = pBase + FM_ADDR2;
  7932. fmd1 = FM_DATA1;
  7933. fmd2 = FM_DATA2;
  7934.  
  7935. /* loop writing and verifying a word at a time: */
  7936. while (bytes)
  7937. {
  7938. /* this duplicates a call to fm_cmd(), for speed: */
  7939. *ptr1 = fmd1;
  7940. *ptr2 = fmd2;
  7941. *ptr1 = FMCMD_WRITE;
  7942.  
  7943. /* write two bytes: */
  7944. *pDst = *pSrc;
  7945. /* do this here - a bit of parallel processing: */
  7946.  
  7947. bytes -= 2;
  7948. val = *pSrc;
  7949.  
  7950. /* loop checking for write completion: */
  7951. for (fail_safe = FMInfo.MaxWrite_ * W_FS_ITER; --fail_safe; )
  7952. if (*pDst == val)
  7953. break; /* done! */
  7954.  
  7955. /* if loop was exhausted, go handle the error: */
  7956. if (fail_safe == 0)
  7957. { fm_tofs_error(pDst, val, *pDst, 4);
  7958. return 0;
  7959. }
  7960.  
  7961. /* move to the next location and loop back: */
  7962. ++pSrc;
  7963. ++pDst;
  7964. }
  7965.  
  7966. /* return happy: */
  7967. return 1;
  7968. }
  7969. /* End of File */
  7970.  
  7971. Listing 7 Main programming routine
  7972. /**********************************************************************
  7973. Write data to the Flash Memory. Returns non-zero on success; on error
  7974. zero is returned and fm_err contains failure data. The pDst and length
  7975. parameters should be word aligned.
  7976.  
  7977. The pScrBuf parameter determines the fate of words erased in a sector but
  7978. not written with new data:
  7979. if (pScrBuf == NULL), then writes to partial sectors leave the
  7980. unwritten words of those sectors erased & unwritten.
  7981. if (pScrBuf! = NULL), then *pScrBuf is assumed to point to at least
  7982. FMINFO.SectorSize bytes, and unwritten (but erased) words
  7983. in partially written sectors will be saved (in the scratch
  7984. buffer) and re-written, maintaining their prior values.
  7985. **********************************************************************/
  7986.  
  7987. fm_write(pbase, pDst, pSrc, length, pScrBuf)
  7988. unsigned short *pbase; /* ptr to the base of the Flash memory */
  7989. unsigned short *pDst; /* where to write in Flash Memory space */
  7990. unsigned short *pSrc; /* bytes from here are copied to Flash */
  7991. size_t length; /* copy this many bytes from pSrc to pDst */
  7992. unsigned short *pScrBuf; /* scratch buffer for partial sector writes */
  7993. {
  7994. size_t wri_len; /* # partial sector bytes of user data */
  7995. size_t keep_before; /* # partial sector bytes prior to user data */
  7996. size_t keep_after; /* # partial sector bytes after user data */
  7997. size_t offset; /* word offsets for various uses */
  7998. size_t SectorAddrMask; /* converts ptr to offset within a sector */
  7999. unsigned short *pSector; /* ptr to even sector boundary */
  8000. char errcode; /* non-zero indicates a parameter error */
  8001.  
  8002. /* share this with other routines in this module: */
  8003. pBase = pbase;
  8004.  
  8005. /* clear out error structure: */
  8006.  
  8007. memset(&fm_err, 0, sizeof(fm_err));
  8008.  
  8009. /* reset the chip and load FMInfo with info about it: */
  8010. if (!fm_status(pBase, (FMINFO *) 0))
  8011. return 0;
  8012.  
  8013. /* check input parameters for word alignment and overflow: */
  8014. errcode = 0;
  8015. if (((size_t) pDst & 1) (length & 1))
  8016. errcode = 6;
  8017.  
  8018. else if (pDst + (length >> 1) pBase + (FMInfo.TotalSize>> 1))
  8019. errcode = 7;
  8020.  
  8021. /* these errors return length in pieces in the exp and act fields: */
  8022. if (errcode)
  8023. { fm_error(pDst, (unsigned short) length >> 8,
  8024. (unsigned short) length & 0xFFFF, errcode);
  8025. return 0;
  8026. }
  8027.  
  8028. /* this masks off address bits indicating offsets into a sector: */
  8029. SectorAddrMask = FMInfo.SectorSize - 1;
  8030.  
  8031. /* see if we are starting on an even sector boundary: */
  8032. keep_before = ((size_t) pDst) & SectorAddrMask;
  8033.  
  8034. /* if the first sector is a partial, do it separately: */
  8035. if (keep_before)
  8036. {
  8037. /* get a pointer to the start of the sector: */
  8038. pSector = pDst - (keep_before)) 1);
  8039.  
  8040. /* compute # bytes in this sector being written by user: */
  8041.  
  8042. wri_len = FMInfo.SectorSize - keep_before;
  8043. if (length < wri_len)
  8044. wri_len = length;
  8045.  
  8046. /* compute # bytes to keep after area being written: */
  8047. keep_after = FMInfo.SectorSize - keep_before - wri_len;
  8048.  
  8049. /* read existing Flash sector into scratch buffer: */
  8050. if (pScrBuf)
  8051. memcpy(pScrBuf, pSector, FMInfo.SectorSize);
  8052.  
  8053. /* erase the sector: */
  8054. if (!fm_sector_erase(pDst, pDst))
  8055. return 0;
  8056.  
  8057. /* copy the user's data to Flash: */
  8058. if (!fm_cpy(pDst, pSrc, wri_len))
  8059. return 0;
  8060.  
  8061. /* restore data in sector before user's data: */
  8062.  
  8063. if (pScrBuf)
  8064. if (!fm_cpy(pSector, pScrBuf, keep_before))
  8065. return 0;
  8066.  
  8067.  
  8068. /* restore data in sector after user's data: */
  8069. if (pScrBuf && keep_after)
  8070. { offset = (wri_len + keep_before) >> 1;
  8071. if (!fm_cpy(pSector + offset,
  8072. pScrBuf + offset, keep_after))
  8073. return 0;
  8074. }
  8075.  
  8076. /* bump pointers: */
  8077. pDst += (wri_len >> 1);
  8078. pSrc += (wri_len >> 1);
  8079. length -= wri_len;
  8080.  
  8081. /* quit if done: */
  8082. if (!length)
  8083. return 1;
  8084. }
  8085.  
  8086. /* see if we are ending on an even sector boundary: */
  8087. wri_len = length & SectorAddrMask;
  8088.  
  8089. /* handle partial final sector: */
  8090. if (wri_len)
  8091. {
  8092. /* get a pointer to the start of the final sector: */
  8093. pSector = pDst + ((length - wri_len) >> 1);
  8094.  
  8095. /* compute # bytes to keep after area being written: */
  8096. keep_after = FMInfo.SectorSize - wri_len;
  8097.  
  8098. /* read partial existing Flash sector into scratch buffer: */
  8099. if (pScrBuf)
  8100. memcpy(pScrBuf, pSector + (wri_len >> 1), keep_after);
  8101. }
  8102. /* erase the remaining sector(s): */
  8103.  
  8104. if (!fm_sector_erase(pDst, pDst + (length >> 1) - 1))
  8105. return 0;
  8106.  
  8107. /* copy the user's data to Flash: */
  8108. if (!fm_cpy(pDst, pSrc, length))
  8109. return 0;
  8110.  
  8111. /* restore data in sector after user's data: */
  8112. if (wri_len && pScrBuf && keep_after)
  8113. { offset = wri_len >> 1;
  8114. if (!fm_cpy(pSector + offset, pScrBuf, keep_after))
  8115. return 0;
  8116. }
  8117.  
  8118. /* return happy: */
  8119. return 1;
  8120. }
  8121. /* End of File */
  8122.  
  8123.  
  8124.  
  8125.  
  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.  
  8165.  
  8166.  
  8167.  
  8168.  
  8169.  
  8170.  
  8171.  
  8172.  
  8173.  
  8174.  
  8175.  
  8176.  
  8177.  
  8178.  
  8179.  
  8180.  
  8181.  
  8182.  
  8183.  
  8184.  
  8185.  
  8186.  
  8187.  
  8188.  
  8189.  
  8190. Using Associative Arrays
  8191.  
  8192.  
  8193. Michael McClung
  8194.  
  8195.  
  8196. Michael McClung is a senior staff engineer at Intecom Inc. He worked as a
  8197. software contractor for 8 years before reentering the corporate work force. He
  8198. received his Master of Science in Computer Science from the University of
  8199. California at Lawrence Livermore Laboratory. His main interests include
  8200. real-time systems, software development processes, and object-oriented
  8201. programming. He can be reached at 214-447-8060 or mm@intecom.com.
  8202.  
  8203.  
  8204.  
  8205.  
  8206. Introduction
  8207.  
  8208.  
  8209. Associative arrays are well known in the software community. They are built
  8210. into some languages such as AWK, Perl, and Smalltalk (as the data dictionary),
  8211. and it is not unusual to find them in C++ class libraries since the language
  8212. supports them well. You can easily find references to them by browsing through
  8213. programming books. Yet, in my experience, associative arrays are not commonly
  8214. used in C programming environments. This article defines associative arrays,
  8215. shows where they can be useful, and presents an implementation in C.
  8216.  
  8217.  
  8218. Defining Associative Arrays
  8219.  
  8220.  
  8221. Interpretations may vary, but in this context the term associative array
  8222. refers to an array that takes a user-defined type as an array index. Viewing
  8223. all arrays as mapping functions, a conventional array is a function that maps
  8224. only from an integer index to another type. For example
  8225. a[5]= 19.58 maps the integer 5 to the floating point number 19.58.
  8226. b[7]= "twelve" maps the integer 7 to the string "twelve".
  8227. c[9]='D' maps the integer 9 to the char 'D'.
  8228. Associative arrays remove the restriction of integer indexes, allowing
  8229. arbitrary data types to directly map to other types, as in the following:
  8230. a["first"] = 19.59 maps the string "first" to the floating point number 19.58.
  8231. b[1.21]="twelve" maps the floating point number 1.21 to the string "twelve".
  8232. c[employee_rec]=&d_rec maps the structure employee_rec to the record pointer
  8233. &d_rec.
  8234. The sparse array exemplifies another type of mapping problem that lends itself
  8235. well to associative arrays. Suppose several points must be identified in a
  8236. 10,000-meter cube. The simplest model is a three-dimensional array with each
  8237. dimension ranging from zero to 10,000. Allocation of a conventional linear
  8238. array far exceeds available memory. With an associative array, which does not
  8239. depend on linear storage, the memory requirements are much smaller. The
  8240. following code, using the associative array CUBEPOINT, requires only the
  8241. locations used, one for each assignment.
  8242. CUBEPOINT(1,123,1564)=TRUE;
  8243. CUBEPOINT(100,256,5647)=TRUE;
  8244. CUBEPOINT(1000,6123,9156)=TRUE;
  8245. Associative arrays are an abstraction, not a particular data structure or
  8246. algorithm, since many different implementations are possible.
  8247.  
  8248.  
  8249. Design Requirements
  8250.  
  8251.  
  8252. Since there are lots of ways to implement associative arrays, I've established
  8253. some criteria to narrow down the choices. In this section I describe these
  8254. criteria, which are efficiency, a generic implementation, and an intuitive
  8255. interface.
  8256.  
  8257.  
  8258. An Efficient Algorithm
  8259.  
  8260.  
  8261. The algorithm must be efficient in both execution speed and memory usage. Two
  8262. primary criteria are efficient insertions of new elements and fast searches
  8263. for existing elements. In addition, I want neither the amount of storable data
  8264. nor the form to be strictly limited. Two excellent algorithms in terms of
  8265. efficiency are AVL trees and hashing. AVL trees, which maintain a balanced
  8266. search tree, provide the best memory handling characteristics. Hashing, which
  8267. usually involves a simpler algorithm, provides a search speed hard to match.
  8268. In this article, I present a design that uses hashing, mainly because its ease
  8269. of implementation makes the example code more clear.
  8270.  
  8271.  
  8272. A Generic Implementation
  8273.  
  8274.  
  8275. A practical implementation must be generic in the sense that a single module
  8276. must handle arbitrary user-defined types and array sizes. I accomplish this in
  8277. two ways. First, the definition of each array is self-contained in an array
  8278. identifier. This encapsulation of the complete definition by an identifier
  8279. allows independent arrays to coexist. Second, I categorize the index for each
  8280. array definition as either a string type or raw binary type. Knowing how to
  8281. read, compare, and copy only these two types, I can implement a generic hash
  8282. algorithm. The user specifies a key's type (string or binary) when the array
  8283. is created.
  8284.  
  8285.  
  8286. An Intuitive Interface
  8287.  
  8288.  
  8289. Associative array access should resemble conventional array access as much as
  8290. possible to ease use and improve readability. The lack of language support for
  8291. generic functions and operator overloading exacerbates the problem of creating
  8292. a good interface. In part, I sidestep the interface requirement by pushing
  8293. some of the responsibility onto the user. Wrapping the access function with a
  8294. user macro provides a respectable, albeit inelegant, associative array
  8295. interface.
  8296.  
  8297. Ideally, an associative array of file descriptor pointers, each indexed by
  8298. file name, would be accessed as follows:
  8299. fd["filexy"]=filedesc;
  8300. Because C does not support this extension, the actual interface is more
  8301. formidable:
  8302. *((FILE **)aa_addr(fd,"filexy")=filedesc;
  8303. However, by adding a macro wrapper, users can approximate the look of
  8304. conventional arrays:
  8305. #define FD(n) (*((FILE **)aa_addr(fd.n))
  8306. . . . .
  8307. FD("filexy")=filedesc;
  8308. Since the compiler's preprocessor does not allow dynamic macro generation, the
  8309. user must define an access macro for each array definition. I explain these
  8310. macros further in the examples.
  8311.  
  8312.  
  8313. The Code
  8314.  
  8315.  
  8316. Two source files contain the associative array code. Listing 1 shows the
  8317. header file that must be included by the application. This file defines the
  8318. associative array structure, declares each entry point, and provides macro
  8319. definitions. The second file contains the module's source, which must be
  8320. compiled and linked with the application. Throughout the description of the
  8321. source, I use the term key to refer to the array index.
  8322. Listing 2 through Listing 7 break the module's source into functional parts,
  8323. each separately explained below. For publication I have kept in-line
  8324. documentation to a minimum, but the accompanying text should clarify the
  8325. purpose of each code section.
  8326.  
  8327.  
  8328. Part 1: The Module Header
  8329.  
  8330.  
  8331. Listing 2 shows the module's header file, which defines macros and makes
  8332. declarations for the rest of the module.
  8333.  
  8334.  
  8335. Part 2: Creating the Array
  8336.  
  8337.  
  8338. The aa_create function in Listing 3 creates an associative array. Creating an
  8339. array entails allocating a structure (aa), loading the structure with all
  8340. values describing the array characteristics, and returning the structure
  8341. pointer. The calling function must save the structure pointer as an identifier
  8342. for all other operations on the array. The caller should treat the identifier
  8343. as an abstract data type, never directly accessing the structure. If an error
  8344. occurs during creation, aa_create returns a null identifier.
  8345. The identifier structure, AA, contains eight fields that make up a complete
  8346. definition. type indicates whether the key is a string type or binary type.
  8347. (This distinction is required since string operations, like compare and copy,
  8348. are different from binary memory operations.) key_size gives the key's size in
  8349. bytes. The key_size parameter is meaningful only with binary keys since string
  8350. sizes are automatically determined with the strlen function. data_size
  8351. specifies the maximum size in bytes of each array element. Two internal tables
  8352. of pointers, keys and data, point respectively to a key and its corresponding
  8353. data element; these two tables maintain the actual contents of the associative
  8354. array. max_elements indicates the size of the keys and data tables, the
  8355. maximum number of elements the array can hold, barring memory limitations. The
  8356. last field, hash_function, points to the hash function that operates on the
  8357. key to produce the hash value. This field, usually not user-defined, is
  8358. discussed further in Part 4.
  8359. Shown at the end of Listing 3 is the prime_size function used by aa_create and
  8360. later by aa_resize. This function forces the array size to be prime to improve
  8361. the efficiency of the hash algorithm (see Part 4).
  8362.  
  8363.  
  8364. Part 3: Accessing the Array
  8365.  
  8366.  
  8367. Once an associative array has been created with aa_create, programs can index
  8368. it with the aa_addr function in Listing 4. aa_addr takes two parameters: an
  8369. associative array identifier and a key. The key indexes the associative array
  8370. to find the corresponding data element. When the data element is found,
  8371. aa_addr returns its address to the calling function, which can directly store
  8372. into or retrieve from the data area.
  8373. Since aa_addr adds new keys to the associative array, it also monitors the
  8374. amount of free space left. If the array becomes too full -- 75 percent in the
  8375. current implementation -- this function dynamically expands the size by 50
  8376. percent. Maintaining a sufficient number of empty slots in the array's hash
  8377. tables insures an efficient search. I chose 75 percent as the critical number
  8378. since studies show a rapid degradation in efficiency as the table load
  8379. approaches 80 percent [1]. Other hash algorithms have better behavior when
  8380. approaching capacity, but at a tradeoff of other factors, such as complexity.
  8381. Part 6 describes the function that changes the array size.
  8382. After checking the array size, aa_addr calls hashindex to find the index
  8383. corresponding to the specified key. Once found, the same index offsets into
  8384. both the keys table and data table. If the keys table already contains the
  8385. sought after key, aa_addr returns the corresponding data address, completing
  8386. the function. If the key is not in the table, aa_addr must allocate memory,
  8387. save the key, and then return the data address. Saving the key guarantees the
  8388. same data address is returned for future accesses with identical keys.
  8389.  
  8390.  
  8391. Part 4: Hashing the Keys
  8392.  
  8393.  
  8394. The whole module pivots on the hashindex function shown in the second part of
  8395. Listing 5. This function, along with the subordinate function, hash_function,
  8396. implements the hash algorithm. This particular hash implementation is called
  8397. Double Hashing with Linear Probe. (See the sidebar "Common Hashing Techniques"
  8398. for an overview of hashing techniques.)
  8399. hashindex calls the hash function for the starting search location. hashindex
  8400. then continues its search until it finds either a match for the input key or
  8401. an empty slot. (An empty slot indicates the key is not in the table.) When the
  8402. hashindex makes comparisons for matching keys it must consider whether a
  8403. binary or string key is used (i.e., whether to use memcmp or strcmp). If the
  8404. search was not successful on the first attempt, hashindex makes a second call
  8405. to the hash function using a different size parameter to give the effect of a
  8406. different hash calculation. If its search is still not successful, hashindex
  8407. continues with a linear search until it finds either a matching key or an
  8408. empty slot. On success, hashindex returns the index of the matching or empty
  8409. slot.
  8410. A good hash function is critical to the efficiency of the hash algorithm.
  8411. Unless the caller defines a custom hash function when the array is created,
  8412. the program will use hash_function by default. The user may wish to define a
  8413. custom hash function for one of two reasons. The first reason is for speed,
  8414. since the function can be optimized for the specific key type. The second and
  8415. most critical reason is to handle non-continuous keys. A non-continous key is
  8416. a key that contains sections of non-data bytes.
  8417. An example of a non-continuous key is a record structure with multiple
  8418. character strings; the data after each string's terminator is undefined,
  8419. leaving gaps in the key. A more subtle example is a complex key type that the
  8420. compiler breaks up to fit on word boundaries. This behavior is compiler
  8421. dependent and should be checked if in doubt. (Dumping memory with the debugger
  8422. is an easy way.) A non-continuous key can corrupt the calculation of the hash
  8423. value by introducting random data. If the key is not guaranteed to be
  8424. continuous, the caller must either provide a custom hash function or
  8425. initialize the key before use. A custom function can bypass any compiler
  8426. dependencies since it knows the structure of the key. Setting the complete key
  8427. to zeros (as with memset(key, 0, sizeof(key)) also resolves the problem by
  8428. initializing any memory gaps to a consistent value.
  8429. hash_function works well on both binary and string keys. The implementation
  8430. sacrifices some clarity for the sake of efficiency since this function more
  8431. than any other affects the speed of the module.
  8432. hash_function builds a shift table the first time it is invoked. The shift
  8433. table helps guarantee that similar keys, such as "x-coordinate" and
  8434. "y-coordinate," do not produce similar hash values. hash_function uses static
  8435. table initialization since the size must be large enough to eliminate error
  8436. checking in the hash calculation.
  8437. Two for loops calculate the hash value, the first for string keys and second
  8438. for binary keys. Each loop takes the exclusive-OR of all bytes in the key,
  8439. with each byte first being modified by a shift value. Only bytes up to the
  8440. null terminator are used for string keys. The aim of both loops is to minimize
  8441. machine cycles while getting a good hash number.
  8442. The last and essential part of creating the hash value is performing division
  8443. with the modulo operator. Division completes the randomization of the hash
  8444. number and bounds it to the size of the hash table. This is where a prime
  8445. table size pays off; taking the modulo of a prime number gives a much better
  8446. hash distribution.
  8447.  
  8448.  
  8449. Part 5: Retrieving the Keys
  8450.  
  8451.  
  8452. aa_keys, in Listing 6, returns all keys in an associative array. Because
  8453. associative array indexes are not ordered, this function must provide a means
  8454. of sequentially visiting every element in the array. The array identifier is
  8455. the input parameter for aa_keys; it outputs an array of key pointers and the
  8456. array's size. The output array is allocated dynamically so it must be freed by
  8457. the caller, but individual key pointers should be considered constants since
  8458. they point into the hash table. The keys are gathered by walking through every
  8459. entry in the keys table. If the entry contains a valid key, neither null nor
  8460. deleted, its pointer is saved in the output array.
  8461.  
  8462.  
  8463.  
  8464. Part 6: Resizing the Array
  8465.  
  8466.  
  8467. aa_resize, shown in Listing 7, expands or shrinks the size of the associative
  8468. array. aa_resize is a module entry point and is also called internally from
  8469. aa_addr when the array becomes too full. My original implementation of this
  8470. module did not allow the array size to be changed, but I found myself wasting
  8471. memory for worst-case allocations. The user still has the option of avoiding
  8472. the resize overhead by allocating a large enough array at creation time.
  8473. No short-cut exists to change the size of a hash table since the size is an
  8474. integral part of the hash function. When a hash table is resized, aa_resize
  8475. must allocate complete new tables and rehash every valid key in the old table
  8476. to a new index location. aa_resize moves the key and associated data pointers
  8477. to the new locations in the new tables. If the new tables cannot be allocated
  8478. or the requested size is too small, the function fails.
  8479.  
  8480.  
  8481. Part 7: Deleting and Testing Elements
  8482.  
  8483.  
  8484. Listing 8 contains two functions: aa_delete removes an element from the array,
  8485. and aa_defined tests if a key is already in the array.
  8486. aa_delete takes two parameters, the array definition and the key to be
  8487. deleted. The deletion frees both the key and the data memory. A flag,
  8488. DELETED_KEY, is stored in the key pointer to prevent the slot from being
  8489. reused. The pointer cannot appear empty since hashindex assumes the search is
  8490. complete when an empty slot is found. If multiple keys hashed to the same
  8491. index as the deleted key, those keys stored after the one deleted would never
  8492. be reached since an empty slot would be found first. This aspect of key
  8493. deletion is inherent to the hashing algorithm used.
  8494. aa_defined returns a true value if the specified key is already stored in the
  8495. array. Users should call aa_defined to find out if the key they're looking for
  8496. is in the table. The application should never examine the data returned from
  8497. aa_addr to determine if a key is contained in the array, since aa_addr always
  8498. adds new keys to the array.
  8499.  
  8500.  
  8501. Examples
  8502.  
  8503.  
  8504. Listing 9 and Listing 10 show two complete programs. These programs link with
  8505. the associative array module and standard libraries, but are otherwise self
  8506. contained. The first program counts the frequency of words read from standard
  8507. input and prints a frequency list. The second program shows an associative
  8508. array implementation of a sparse array.
  8509. The user-defined macros, WORD_COUNT and SPARSE, simplify access to the arrays.
  8510. A simple rule to follow when creating these macros is to cast the return value
  8511. of aa_addr to a pointer to the array's data type and encase this expression
  8512. with the dereference operator. Dereferencing the returned address allows the
  8513. macros to be transparently used with nearly all operators. The user may
  8514. optionally use the macro, AA_ACCESS, to create the access macro. AA_ACCESS is
  8515. defined for this purpose in the header file.
  8516.  
  8517.  
  8518. Conclusion
  8519.  
  8520.  
  8521. Associative arrays are a general purpose abstraction fitting a wide range of
  8522. problems. Effectively incorporating higher abstractions into our programming
  8523. solutions provides a path to lower cost and complexity. A few examples have
  8524. shown that associative arrays do simplify programs. My own use of associative
  8525. arrays has expanded as I've grown more aware of the range of possibilities.
  8526. I have used associative arrays in applications ranging from development tools
  8527. running in batch mode to production code in real-time systems. Conventional
  8528. arrays access data elements only through integer indexes, but associative
  8529. arrays allow arbitrary types, such as strings, to index the array. This
  8530. seemingly small but powerful addition in capability has made associative
  8531. arrays a mainstay in many of my solutions to programming problems.
  8532.  
  8533.  
  8534. Bibliography
  8535.  
  8536.  
  8537. [1] Knuth, Donald E. The Art of Computer Programming, Volume 3: Sorting and
  8538. Searching. Addison-Wesley, 1973.
  8539. Common Hashing Techniques
  8540. Hashing is an alternative approach to search algorithms that rely on key
  8541. comparison. Hashing bases its search on a mathematical calculation performed
  8542. on the search key. The mathematical calculation, known as the hash function,
  8543. calculates the data location using the key as the function input.
  8544. As an example, assume a book is stored by ISBN number, such as ISBN
  8545. 0-19-502402-8.
  8546. If the ISBN number is treated as an integer and a simple hash function is
  8547. defined as f(ISBN)=ISBN modulo 500, the location of each book in a data table
  8548. is determined by inputting its ISBN number into the function. Since the hash
  8549. function indexes the data table, the function's range must correspond to the
  8550. size of the table.
  8551. The above hashing function by itself has a flaw since nothing prevents the
  8552. hash function from selecting the same location for two different books;
  8553. duplicate indexes occur if two ISBN numbers differ by a multiple of 500.
  8554. Duplicate hash values for different keys are known as collisions in hashing
  8555. terminology. You can handle hash collisions one of two ways: 1) prevent the
  8556. occurrence of collisions by using a perfect hash function, or 2) use
  8557. additional processing to handle collisions when they occur.
  8558. Perfect hash functions, which produce no collisions, can only be derived when
  8559. the number of keys is very small and the keys themselves are fixed, as in a
  8560. compiler's list of key words. Since collisions cannot be avoided for most
  8561. applications, writers of hash functions concentrate on reducing the frequency
  8562. of collisions, by producing an even distribution of hash values.
  8563. Most hash function calculations are based either on division, as in the modulo
  8564. operation above, or multiplication, in which selected digits, from the product
  8565. of the index and a constant, are combined into a hash value.
  8566. When the key is not an integer, a hash function usually first reduces it to an
  8567. integer form and then inputs it into a hash calculation. For example, a hash
  8568. function for character strings might first exclusive-OR individual characters
  8569. into a cumulative value, then input that value into a modulo operation.
  8570.  
  8571.  
  8572. Handling Collisions
  8573.  
  8574.  
  8575. Hash algorithms can be categorized by the method they use to handle
  8576. collisions. Two common methods are described below.
  8577. Linear probe is the simplest method of handling collisions. If a key hashes to
  8578. a location already containing another value, the linear probe method selects
  8579. the next available empty location. In Figure 1, if 5960010 hashes to location
  8580. 3, the linear probe performs a sequential search that scans the table until it
  8581. finds an empty slot.
  8582. Another common hash algorithm handles collisions by associating a "bucket"
  8583. with each hash value. Two keys colliding at the same location are stored in
  8584. the same bucket (Figure 2). To find a key, the hash function first identifies
  8585. the correct bucket, then compares the residents of the bucket individually
  8586. with the search key. The number of keys stored in any bucket is usually small
  8587. enough such that a linked-list can adequately represent a bucket. A sequential
  8588. search through the linked-list identifies the correct key.
  8589. Figure 1 Linear probe collision handling
  8590. Figure 2 Using hash "buckets" to handle collisions
  8591.  
  8592. Listing 1 Associative Array Header File
  8593. #ifndef _AA_INCLUDED
  8594. #define _AA_INCLUDED
  8595.  
  8596. #include <stdio.h>
  8597. #include <stdlib.h>
  8598. #include <string.h>
  8599.  
  8600.  
  8601. enum AA_KEYTYPE {STRING_KEY, BINARY_KEY};
  8602.  
  8603. typedef struct {
  8604. enum AA_KEYTYPE type;
  8605. size_t key_size;
  8606. size_t data_size;
  8607. void **keys;
  8608. void **data;
  8609. size_t current_elements;
  8610. size_t max_elements;
  8611. int (*hash_function)(void *,int,int,enum AA_KEYTYPE);
  8612. } AA;
  8613.  
  8614. #define AA_MAX_KEY_SIZE 1024
  8615. #define AA_CURRENT_SIZE(aa) (aa->max_elements)
  8616. #define AA_CURRENT_COUNT(aa) (aa->current_elements)
  8617. #define AA_ACCESS(aa_id,type,key) \
  8618. (*((type *)aa_addr(aa_id,key)))
  8619.  
  8620. AA *aa_create (enum AA_KEYTYPE, size_t,
  8621. size_t,size_t,int (*)());
  8622. void *aa_addr(AA *,void *) ;
  8623. void aa_keys(AA *,void ***,int *);
  8624. void aa_delete(AA *,void *);
  8625. unsigned char /* BOOLEAN */ aa_defined(AA *,void *);
  8626. #endif
  8627. /* End of File */
  8628.  
  8629.  
  8630. Listing 2 The Module Header
  8631. /************** Associative Array Module *************/
  8632. /* Entry Points: aa_create, aa_resize, aa_addr, */
  8633. /* aa_keys, aa_delete, aa_defined */
  8634. /* Test Platforms: HP-UX Version 9.01 */
  8635. /* PC-DOS 3.1 with Turbo C++ */
  8636. /*****************************************************/
  8637. #include "aa.h"
  8638.  
  8639. #define DELETED_KEY 1
  8640. #define MAX_KEY_SIZE 1024
  8641. #define NULLKEY(addr) (addr == NULL)
  8642. #define MAXFULL(size) (size * 0.75)
  8643. #define TOO_FULL(count,max) \
  8644. ((count > MAXFULL(max)) ? TRUE : FALSE)
  8645. #define KEY_MATCH(type,key,ptr,size) \
  8646. (type!=STRING_KEY && memcmp(key,*ptr,size)==0)
  8647. #define STRING_KEY_MATCH(t,k,p) \
  8648. (t==STRING_KEY && strcmp((char *)k,*(char **)p)==0)
  8649. #define VALID_KEY(addr) \
  8650. (addr != (void *)NULL && addr != (void *)DELETED_KEY)
  8651. #define INC_SIZE(size) \
  8652. ((size_t)(size * 1.5))
  8653. #define HASH_FUNCTION(a,key,size) \
  8654. (*(a->hash_function))(key,a->key_size,size,a->type)
  8655. #define KEYSIZE(aa,key) \
  8656. (aa->type==STRING_KEY ? strlen(key)+1 : aa->key_size)
  8657. #define KEYCOPY(aa,dest,key) (aa->type==STRING_KEY ? \
  8658. strcpy(*dest,key) : memcpy(*dest,key,aa->key_size))
  8659.  
  8660. #define TRUE 1
  8661. #define FALSE 0
  8662.  
  8663. typedef unsigned char BYTE;
  8664. typedef unsigned char BOOLEAN;
  8665.  
  8666. static int hash_function();
  8667. static size_t prime_size();
  8668.  
  8669. /* End of File */
  8670.  
  8671.  
  8672. Listing 3 Creating the Array
  8673. /*>>>> Create associative array (AA) definition <<<<*/
  8674. AA *aa_create (type,key_size,data_size,size,user_hashf)
  8675. enum AA_KEYTYPE type; /* input -- key type */
  8676. size_t key_size; /* input -- max binary key size */
  8677. size_t data_size; /* input -- max data size */
  8678. size_t size; /* input -- initial size */
  8679. int (*user_hashf)(); /* input -- optional hash func */
  8680. {
  8681. AA * aa;
  8682.  
  8683. /* all AA information stored in structure */
  8684. if (aa=(AA *)malloc(sizeof(AA))) {
  8685. size=prime_size(size);
  8686. aa->keys=(void **)calloc(size, sizeof(void *));
  8687. aa->data=(void **)calloc(size, sizeof(void *));
  8688. if (aa->keys && aa->data) { /*if allocation ok*/
  8689. aa->type=type;
  8690. aa->key_size=key_size;
  8691. aa->max_elements=size;
  8692. aa->current_elements=0;
  8693. aa->data_size=data_size;
  8694. if ((aa->hash_function=user_hashf) == NULL)
  8695. aa->hash_function=&hash_function;
  8696. } else { /* failure, release memory */
  8697. if (aa->keys) free((void *)aa->keys);
  8698. if (aa->data) free((void *)aa->data);
  8699. free((void *)aa);
  8700. aa=NULL; /* return NULL on failure */
  8701. }
  8702. }
  8703. return(aa); /* return AA definition */
  8704. }
  8705.  
  8706. /*>>>> Get 1st prime larger than request size <<<<*/
  8707. static size_t prime_size(size)
  8708. size_t size; /* start search for prime at size*/
  8709. {
  8710. int divisor,i=(size<5 ? 5 : size);
  8711. i=(i % 2) ? i : i + 1; /* start on odd number */
  8712. do {
  8713. /* try each number which could be exact divisor */
  8714. for (divisor=3; divisor <= i/divisor; divisor+=2)
  8715. if ((i % divisor) == 0) break; /* if !prime */
  8716. if (divisor > (i/divisor)) break; /* if prime */
  8717. } while (i+=2); /* try next odd number */
  8718. return(i); /* return prime number */
  8719.  
  8720. }
  8721.  
  8722. /* End of File */
  8723.  
  8724.  
  8725. Listing 4 Accessing the Array
  8726. /*>>>> Return data pointer for specified key <<<<*/
  8727. /* Main interface to store and retrieve AA data */
  8728. void *aa_addr(aa,key)
  8729. AA * aa; /* input -- AA definition */
  8730. void *key; /* input -- lookup key */
  8731. {
  8732. int index;
  8733. void **keyptr, **dataptr;
  8734.  
  8735. if (TOO_FULL(aa->current_elements,aa->max_elements))
  8736. if (!aa_resize(aa,INC_SIZE(aa->max_elements)))
  8737. fprintf(stderr,"AA: resize failed\n");
  8738.  
  8739. index=hashindex(aa,key); /* index for key & data */
  8740. keyptr=&aa->keys[index];
  8741. dataptr=&aa->data[index];
  8742. if (!VALID_KEY(*keyptr)) { /* if key not in table */
  8743. /* allocate key & data memory; copy/define key */
  8744. *keyptr=(void *)malloc(KEYSIZE(aa,key));
  8745. *dataptr=(void *)calloc(aa->data_size,1);
  8746. if (keyptr && dataptr) { /* if allocation ok */
  8747. KEYCOPY (aa, keyptr, key);
  8748. aa->current_elements++;
  8749. } else { /* allocation failed */
  8750. fprintf(stderr,"AA: key allocation failed\n");
  8751. fflush(stderr); /* probably will crash soon */
  8752. }
  8753. }
  8754. return(*dataptr); /* return pointer to data area */
  8755. }
  8756.  
  8757. /* End of File */
  8758.  
  8759.  
  8760. Listing 5 Hashing the Keys
  8761. /*>>>>>>>>>>>> Default hash function <<<<<<<<<<<<<<<*/
  8762. static int hash_function(key,keysize,maxhash,type)
  8763. void *key; /* input -- key to be hashed */
  8764. int keysize; /* input -- size of key */
  8765. int maxhash; /* input -- max legal hash value */
  8766. enum AA_KEYTYPE type; /* input -- type of input key */
  8767. {
  8768. int i;
  8769. static shifts_initialized=FALSE;
  8770. static BYTE svalues[]={0,24,3,21,5,19,7,17,9,15,11};
  8771. static BYTE sconstants[AA_MAX_KEY_SIZE];
  8772. register unsigned long count=0;
  8773. register BYTE *kp=(BYTE *)key, *end;
  8774. register BYTE *shifts=sconstants;
  8775.  
  8776. /* on 1st call init shifts; empirically determined */
  8777. if (!shifts_initialized) {
  8778. for(i=0; i < AA_MAX_KEY_SIZE; i++)
  8779.  
  8780. sconstants[i]=svalues[i % sizeof(svalues)];
  8781. shifts_initialized=TRUE;
  8782. }
  8783.  
  8784. /* the loops are high runners; need to be fast */
  8785. if (type == STRING_KEY)
  8786. for ( ; *kp != (char) 0; kp++,shifts++)
  8787. count ^= *kp << *shifts;
  8788. else
  8789. for (end=kp+keysize; kp < end; kp++,shifts++)
  8790. count ^= *kp << *shifts;
  8791. return (count % maxhash); /* return hash value */
  8792. }
  8793.  
  8794. /*>>> Find index for key; Use hash algorithm. <<<*/
  8795. /*>>> Method: Linear Probe with Double Hashing <<<*/
  8796. static int hashindex (aa,key)
  8797. AA * aa; /* input -- AA definition */
  8798. void *key; /* input -- lookup key */
  8799. {
  8800. int found=FALSE, index;
  8801. void **ptr;
  8802. BOOLEAN first_collision=TRUE;
  8803.  
  8804. /* hash function determines where to start search */
  8805. index=HASH_FUNCTION(aa, key, aa ->max_elements);
  8806.  
  8807. do { /* do until matching key or empty slot found */
  8808. ptr=&aa->keys[index]; /* get key pointer */
  8809.  
  8810. if (NULLKEY(*ptr))
  8811. found=TRUE; /* empty slot */
  8812. /* keys comparisons must consider the key type */
  8813. else if (STRING_KEY_MATCH(aa->type,key,ptr))
  8814. found=TRUE; /* string key found */
  8815. else if (KEY_MATCH(aa->type,key,ptr,aa->key_size))
  8816. found=TRUE; /* non-string key found */
  8817. /* wrong key was found so collision occurred */
  8818. else if (first_collision) {
  8819. /* try double hash on first collision only */
  8820. index=HASH_FUNCTION(aa,key,aa->max_elements-4);
  8821. first_collision=FALSE;
  8822. } else /* collision -- use linear search/probe */
  8823. if (++index == aa->max_elements) /* if end */
  8824. index=0; /* restart search at beginning */
  8825. } while (!found);
  8826.  
  8827. return(index); /* return key and data index */
  8828. }
  8829.  
  8830. /* End of File */
  8831.  
  8832.  
  8833. Listing 6 Retrieving the Keys
  8834. /*>>>>>>>>>>>>> Return all keys in AA <<<<<<<<<<<<<<*/
  8835. void aa_keys(aa,keys,num)
  8836. AA * aa; /* input -- AA definition */
  8837. void ***keys; /* output -- array of key pointers */
  8838. int *num; /* output -- number of keys returned */
  8839.  
  8840. {
  8841. register int i;
  8842. register void **ptr;
  8843. size_t buf_size=aa->current_elements*sizeof(void **);
  8844.  
  8845. if (*keys=(void **)malloc(buf_size)) {
  8846. *num=0; ptr=aa->keys;
  8847. for (i=0; i<aa->max_elements; i++,ptr++)
  8848. if (VALID_KEY(*ptr))
  8849. (*keys)[(*num)++]=*ptr;
  8850. } else /* allocation failed */
  8851. *num=-1;
  8852. }
  8853. /* End of File */
  8854.  
  8855.  
  8856. Listing 7 Resizing the Array
  8857. /*>>>>>>>>>>>>>>>>>> Resize the AA <<<<<<<<<<<<<<<<<*/
  8858. BOOLEAN aa_resize (aa,newsize)
  8859. AA * aa; /* input -- AA definition */
  8860. size_t newsize; /* input -- new size for AA */
  8861. {
  8862. BOOLEAN rc=TRUE;
  8863. void **newkeys, **newdata, **oldkeys, **olddata;
  8864. int i, oldsize, index;
  8865.  
  8866. newsize=prime_size(newsize); /* insure prime size */
  8867. /* alloc new/enlarged key and data tables */
  8868. newkeys=(void **)calloc(newsize, sizeof(void *));
  8869. newdata=(void **)calloc(newsize, sizeof(void *));
  8870. if ((aa->current_elements < MAXFULL(newsize)) &&
  8871. (newkeys && newdata)) {
  8872. oldkeys=aa->keys; /* save old table ptrs */
  8873. olddata=aa->data;
  8874. oldsize=aa->max_elements;
  8875. aa->keys=newkeys; /* put new tables in AA def */
  8876. aa->data=newdata;
  8877. aa->max_elements=newsize;
  8878.  
  8879. /* now add/rehash all keys/data into new table */
  8880. for (i=0; i<oldsize; i++) /*scan for valid keys*/
  8881. if (VALID_KEY(oldkeys[i])) {
  8882. index=hashindex(aa,oldkeys[i]);
  8883. aa->keys[index]=oldkeys[i];
  8884. aa->data[index]=olddata[i];
  8885. }
  8886. free((void *)oldkeys); free((void *)olddata);
  8887. } else { /* cleanup on error */
  8888. if (newkeys) free((void *)newkeys);
  8889. if (newdata) free((void *)newdata);
  8890. rc=FALSE;
  8891. }
  8892. return(rc); /* return status, TRUE if successful */
  8893. }
  8894. /* End of File */
  8895.  
  8896.  
  8897. Listing 8 Deleting and Testing Elements
  8898. /*<<<<<<<<<< Delete an entry from the AA <<<<<<<<<<*/
  8899.  
  8900. void aa_delete(aa,key)
  8901. AA * aa; /* input -- AA definition */
  8902. void * key; /* input -- key to delete, with data */
  8903. {
  8904. int index=hashindex(aa,key);
  8905. if (VALID_KEY(aa->keys[index])) {
  8906. free ((void *)aa->keys[index]);
  8907. aa->keys[index]=(void *)DELETED_KEY;
  8908. free ((void *)aa->data[index]);
  8909. aa->data[index]=NULL;
  8910. aa->current_elements--;
  8911. }
  8912. }
  8913.  
  8914. /*>>>>>> Return TRUE if key defined in the AA <<<<<<<*/
  8915. BOOLEAN aa_defined(aa,key)
  8916. AA * aa; /* input -- pointer to AA definition */
  8917. void * key; /* input -- key to check if defined */
  8918. {
  8919. return(VALID_KEY(aa->keys[hashindex(aa,key)]));
  8920. }
  8921. /* End of File */
  8922.  
  8923.  
  8924. Listing 9 Program to Count Word Frequency
  8925. /* Counts words then displays word frequency. */
  8926. /* Input: one word per line read from stdin */
  8927.  
  8928. #include "aa.h"
  8929.  
  8930. #define WORD_COUNT(word) (*(int *)aa_addr(aa_id,word))
  8931. #define UNUSED 0
  8932.  
  8933. main()
  8934. {
  8935. int i, count;
  8936. char **words, word_buffer[100];
  8937. AA *aa_id=aa_create(STRING_KEY,UNUSED,sizeof(int),500,0);
  8938.  
  8939. while(gets(word_buffer) != NULL)
  8940. WORD_COUNT(word_buffer)++;
  8941.  
  8942. aa_keys(aa_id,(void ***)&words,&count);
  8943. for (i=0; i<count; i++)
  8944. printf(" Word: %-30s Frequency: %d\n",
  8945. words[i],WORD_COUNT(words[i]));
  8946. }
  8947. /* End of File */
  8948.  
  8949.  
  8950. Listing 10 3D Sparse Array Program
  8951. #include "aa.h"
  8952.  
  8953. typedef struct { long i; long j; long k; } INDEX;
  8954. static INDEX t; /* temporary used in SPARSE macro */
  8955. #define SPARSE(x,y,z) (*(t.i=x,t.j=y,t.k=z, \
  8956. (double *)aa_addr(sparse_id,&t)))
  8957. main()
  8958. {
  8959.  
  8960. unsigned int i,j,k;
  8961. AA *sparse_id=aa_create(BINARY_KEY,sizeof(INDEX),
  8962. sizeof(double),500,NULL);
  8963. for (i=0; i<=10000; i+=2000)
  8964. for (j=0; j<=10000; j+=2000)
  8965. for (k=0; k<=10000; k+=2000) {
  8966. SPARSE(i,j,k)=(i+j+k) * 0.0001;
  8967. printf("SPARSE(%5d,%5d,%5d)\t=\t%f\n",
  8968. i,j,k,SPARSE(i,j,k));
  8969. }
  8970. }
  8971. /* End of File */
  8972.  
  8973.  
  8974.  
  8975.  
  8976.  
  8977.  
  8978.  
  8979.  
  8980.  
  8981.  
  8982.  
  8983.  
  8984.  
  8985.  
  8986.  
  8987.  
  8988.  
  8989.  
  8990.  
  8991.  
  8992.  
  8993.  
  8994.  
  8995.  
  8996.  
  8997.  
  8998.  
  8999.  
  9000.  
  9001.  
  9002.  
  9003.  
  9004.  
  9005.  
  9006.  
  9007.  
  9008.  
  9009.  
  9010.  
  9011.  
  9012.  
  9013.  
  9014.  
  9015.  
  9016.  
  9017.  
  9018.  
  9019.  
  9020.  
  9021.  
  9022.  
  9023. Simulating C++ Templates in C and C++
  9024.  
  9025.  
  9026. John W. Small
  9027.  
  9028.  
  9029. Since receiving his BSCS from George Mason University in 1985, John W. Small
  9030. has run PSW/ Power Software, a software tools and consulting company. He has
  9031. authored the FlexList C/C ++, Container Lite C++, and COP (C Object
  9032. Programming) tools as well as EB, the Electronic Book, a hypertext
  9033. application. He has been programming in C/C ++ for 10/5 years, respectively,
  9034. as well as in Smalltalk. He can be reached via email at john.small@wdn.com or
  9035. (703) 759-3838 evenings.
  9036.  
  9037.  
  9038. This article outlines a systematic approach to simulating templates in C and
  9039. C++. You may need to do so because not all C++ translators implement
  9040. templates, and templates are certainly not a part of conventional C. I begin
  9041. by demonstrating why you might want to use function and class templates. Then
  9042. I show how to synthesize function and class templates, with examples in both C
  9043. and C++. If you are familiar only with C, you should learn a little C++ in the
  9044. process.
  9045.  
  9046.  
  9047. Function Templates
  9048.  
  9049.  
  9050. Macros in C and C++ allow you to define generic functions such as:
  9051. #define max(x, y) ((x > y) ? x : y)
  9052. However, using the #define preprocessor directive circumvents any strong type
  9053. checking that might be provided by the translator. And it can also introduce
  9054. undesired side effects for the unsuspecting programmer who assumes max(x, y)
  9055. is a function call instead of a macro call.
  9056. Consider:
  9057. t = max(++r, s);
  9058. which expands to
  9059. t = ((++r > s) ? ++r: s)
  9060. If r is greater than or equal to s upon calling max(++r, s), r will be
  9061. incremented twice. The arguments ++r and s are treated simply as token
  9062. sequences by the max macro. The preprocessor substitutes them for the formal
  9063. parameters x and y wherever they appear in the definition of max. Macros are
  9064. thus said to be called by name. The names of the actual arguments passed to
  9065. the macro replace the formal parameters in the expansion process.
  9066. Such problems can be avoided by coding max as a function:
  9067. int max(int x, int y)
  9068. { return (x > y) ? x : y; }
  9069. With this definition, the expression
  9070. t = max(++r, s);
  9071. always leaves r incremented by one instead of sometimes being incremented by
  9072. two. The max function is said to be called by value. The values of ++r and s
  9073. are stored in the auto (local) variables x and y respectively. The value of r
  9074. is incremented before it is stored in x and it can never be erroneously
  9075. incremented again inside max.
  9076. With this solution however, we have lost the ability to define max
  9077. generically. If we need to compare two float values, we would have to code:
  9078. float max(float x, float y)
  9079. { return (x > y) ? x : y; }
  9080. Both the int and float versions of max can be simultaneously defined in C++.
  9081. Unlike C, the newer language allows function names to be overloaded. The types
  9082. of the arguments in the function call dictate which version the C++ translator
  9083. calls.
  9084. C++ also provides a construct known as a function template that allows us to
  9085. define a generic family of functions without sacrificing strong type checking.
  9086. The C++ template approach to defining max would be;
  9087. template <class TYPE>
  9088. inline TYPE max(TYPE x, TYPE y)
  9089. { return (x > y) ? x : y; }
  9090. A function template is introduced by the keyword template followed immediately
  9091. by one or more formal parameters, enclosed in angle brackets, that specify
  9092. types. The function body is the same as a regular function definition except
  9093. that the template's type parameters can appear anywhere in the definition that
  9094. a data type name might normally appear. The following code causes the
  9095. translator to automatically generate three different versions of the max
  9096. function from its template definition. One definition is for int arguments,
  9097. another is for long arguments, and yet another is for float arguments.
  9098. int r, s, t;
  9099. long u, v, w;
  9100. float x, y, z;
  9101. ...
  9102. t = max(++r, s);
  9103. u = max(++v, w);
  9104. z = max(x, y);
  9105. The keyword inline signals to the translator to generate inline code instead
  9106. of invoking a function call each time. Thus you can see that function
  9107. templates require no more overhead than macros but provide strong type
  9108. checking and preclude erroneous side effects. Furthermore you can manually
  9109. override template functions by supplying your own specialization for some
  9110. combination of arguments:
  9111. inline char *max(char* x, char *y)
  9112. { return (strcmp(x,y) >= 0) ? x : y; }
  9113. With this definition, the code sequence:
  9114. char a[] = "one", b[] = "two", *c;
  9115.  
  9116. c = max(a, b);
  9117. calls your version of max for strings, preventing the translator from
  9118. generating a version from the max template.
  9119.  
  9120.  
  9121. Class Templates
  9122.  
  9123.  
  9124.  
  9125. C++ also provides a construct known as a class template, for which there is no
  9126. direct counterpart in C. Suppose you need to implement a stack of strings in C
  9127. (see Listing 1). Our stack of strings includes functions for all the stack
  9128. primitive operations, e.g. full, push, top, and pop. The init function is used
  9129. to initialize a string stack. The main function demonstrates stack usage by
  9130. building a "To Do" list, which is then streamed to the standard output. To
  9131. implement a stack for some other data type you would have to clone this code,
  9132. replacing char * throughout with the desired data pointer type. Of course all
  9133. occurrences of the name StrStack would also have to be replaced with the
  9134. appropriate new stack name.
  9135. The C++ version of Listing 1 is shown in Listing 2. The first thing you should
  9136. notice is that C++ automatically makes a structure tag a type definition as
  9137. well, so that StrStack is now a type. Thus:
  9138. class StrStack { ... };
  9139. is equivalent to:
  9140. typedef class StrStack { ... } StrStack;
  9141. Also, in C++ the keyword class is equivalent to the keyword struct except the
  9142. members of the structure are private in scope by default and thus cannot be
  9143. accessed from outside the class.
  9144. The second thing you should notice is that the functions full, push, top, and
  9145. pop are now public methods of the StrStack class. They don't make the stack
  9146. structure any larger, but the C++ translator can now keep track of which
  9147. functions go with which data structures. The public functions provide a secure
  9148. interface to the hidden object members, providing an important form of
  9149. encapsulation. Users of the StrStack class cannot accidently corrupt the
  9150. member objects in a StrStack class instance (object), for example.
  9151. Furthermore, all member functions have an implicit this pointer to a class
  9152. instance as a hidden formal parameter. To the translator, the definition
  9153. char *pop()
  9154. {
  9155. return (items ? itemPtrs[--items]
  9156. : 0);
  9157. }
  9158. really behaves like:
  9159. char *pop(StrStack *this)
  9160. {
  9161. return (this->items ?
  9162. this->itemPtrs [--this->items]
  9163. : 0);
  9164. }
  9165. Thus, pop implicitly knows which StrStack instance it is going to pop.
  9166. Sometimes the implicit this pointer may need to be explicitly used in an
  9167. expression to distinguish between a data member and a formal parameter or some
  9168. other object, when their names conflict.
  9169. Member functions defined within the class declaration are considered inline
  9170. functions. The function StrStack::push(char *) is not inlined since its
  9171. definition appears outside its class declaration.
  9172. The function StrStack() is a constructor for the class, equivalent to the C
  9173. version's init function. Constructors are used to initialize class instances
  9174. at their point of definition. Thus
  9175. StrStack ToDo;
  9176. appearing in the function main not only allocates memory for the ToDo stack
  9177. but causes the translator to automatically generate a call to StrStack() for
  9178. you.
  9179. Lastly, notice how methods are invoked:
  9180. while (ToDo.top())
  9181. The implicit this pointer of top is automatically set to point to ToDo. The
  9182. C++ version of top() doesn't have to check to see if this is a null pointer.
  9183. By contrast, the C version primitives must always validate stackPtr.
  9184. Listing 3 shows how a class template can be used to parameterize the stack
  9185. class so that it can be reused for any data type without requiring the
  9186. programmer to clone code. The class template is also introduced by the keyword
  9187. template followed immediately by one or more formal parameters, enclosed in
  9188. angle brackets, specifying types or constants. We now use the expression:
  9189. stack<char> ToDo;
  9190. to define our ToDo stack of strings (or character pointers). Since the
  9191. template has no second actual parameter, a default value of 5 is used to limit
  9192. the stack to 5 strings. Defaults can currently be specified only for
  9193. constants, never types. Thus constant parameters with default arguments must
  9194. always appear last in a template's formal parameter list. When generating a
  9195. template class, the translator substitutes the actual parameters supplied or
  9196. defaulted for the template's formal parameters throughout the template model.
  9197. A second stack:
  9198. stack<int, 10> CountDown;
  9199. defines a stack of integer pointers limited to a maximum of ten items. Upon
  9200. encountering a new class specification, the translator will only generate the
  9201. parameterized class if it hasn't already been generated. Please note that a
  9202. class template's out-of-line member function definition can appear in a header
  9203. file, since it is only a model with which the translator can generate the
  9204. actual function definition. Hence a template function definition, in itself,
  9205. generates no code. Template generation is actually a two-part process,
  9206. consisting of generating declarations and definitions.
  9207.  
  9208.  
  9209. Synthesizing Templates in C++
  9210.  
  9211.  
  9212. When you invoke a template, it automatically generates both declarations and
  9213. definitions. For example, a function declaration names the function and
  9214. describes its required parameters, while its definition is an algorithmic
  9215. description for which the translator must generate code. Likewise, a data
  9216. structure declaration names its type and specifies its layout but doesn't yet
  9217. reserve any storage. Only when an object is associated for that data type
  9218. might memory be allocated for it.
  9219. The storage class extern turns what would otherwise be an object definition
  9220. into an object declaration -- the object has external linkage with memory
  9221. allocated for it elsewhere. A function declaration without a defining body
  9222. generates no code regardless of the linkage you specify. An inline function
  9223. has a defining body, but generates no code until it is called in a context
  9224. that is not itself an inline function. For our purpose here, I will call all
  9225. these declarations, because they only describe. By contrast, definitions
  9226. generate code or reserve storage for objects.
  9227. In our synthesis of templates, we need to emulate separately the process of
  9228. generating declarations and definitions. Since declarations typically appear
  9229. in header files while definitions appear in source files, our approach will
  9230. place the declarations in a header file and the definitions in a source file.
  9231. The logic for this will be apparent shortly. Listing 4, Listing 5, and Listing
  9232. 6 show how both function and class templates are synthesized for the function
  9233. max and the class stack. Listing 4 is the file that generates declarations,
  9234. Listing 5 the file that generates definitions, and Listing 6 is the source
  9235. file where these emulated templates are used. We'll call our approach to
  9236. emulating templates "form templates."
  9237.  
  9238.  
  9239. Layout Rules
  9240.  
  9241.  
  9242. 1) Since more than one form template may appear in a generating file, each
  9243. form template must be encased within its own conditional preprocessor
  9244. directive, as in:
  9245. #ifdef NAME /* open NAME envelope */
  9246. ...
  9247. #endif /* close NAME envelope */
  9248. which I call a form-template envelope. Here, NAME is the name of the function
  9249. or class form template. For C++, function form template envelopes are named
  9250. the same as their first template parameter. See if you can pick out the form
  9251. template envelopes in Listing 4 and Listing 5.
  9252. 2) Form template parameter names must be unique within a generating file. For
  9253. example, the following C++ templates:
  9254. template <class max_TYPE>
  9255. inline max_TYPE max(max_TYPE x, max_TYPE y)
  9256. { return (x > y) ? x : y; }
  9257.  
  9258. template <class stack_ITEM,
  9259.  
  9260. unsigned stack_MAX_ITEMS = 5>
  9261. class stack { ... }
  9262. have no naming conflicts among their various parameter names -- max_TYPE,
  9263. stack_lTEM, and stack_MAX_ITEMS. Though not a requirement for C++ templates,
  9264. it is for form templates.
  9265. 3) A default value for a constant parameter must be conditionally defined as
  9266. its default value before its class declaration. Both the default value and
  9267. class declaration must be contained within the form template envelope:
  9268. /* open form template envelope */
  9269. #ifdef stack
  9270.  
  9271. /* define default value */
  9272. #ifndef stack_MAX_ITEMS
  9273. #define stack_MAX_ITEMS 5
  9274. #endif
  9275.  
  9276. class stack {
  9277. stack_ITEM *itemPtrs[stack_MAX_ITEMS];
  9278. ...
  9279. #endif /* close form template envelope */
  9280. 4) A form template doesn't utilize the keyword template, nor is there a formal
  9281. parameter list. Otherwise the form template model is exactly the same as the
  9282. standard C++ template model. There is one additional exception: scope
  9283. resolution names must drop their parameterizing arguments. For example:
  9284. int stack<stack_ITEM, stack_MAX_ITEMS>::
  9285. push(stack_ITEM *itemPtr)
  9286. is changed to read
  9287. int stack::push(stack_ITEM *itemPtr)
  9288. 5) A form template header file, such as Listing 4, always concludes with the
  9289. conditional parameter wrapup section. This section undefines all form template
  9290. parameters and names unless the header is being included by its corresponding
  9291. form template source file, the file that generates definitions. For example:
  9292. /* decl_gen.hpp */
  9293. ....
  9294. /* parameter wrapup section */
  9295. #ifndef def_gen_cpp /* defined in def_gen.cpp */
  9296. #undef max_TYPE
  9297. #undef stack_ITEM
  9298. #undef stack_MAX_ITEMS
  9299. #undef stack
  9300. #endif
  9301. 6) A form template source file, such as listing 5, must first include its
  9302. corresponding header file. For example:
  9303. /* def_gen.cpp */
  9304. #define def_gen_cpp
  9305. #include "decl_gen.hpp"
  9306. #undef def_gen_cpp
  9307. Notice how the include directive is sandwiched between the definition and
  9308. undefinition of the source-file macro id. This is the macro id used to exclude
  9309. the parameter wrapup section described previously in rule 5.
  9310. 7) Much like the form template header file, the source file terminates with a
  9311. parameter wrapup section. In this case, however, the section is unconditional.
  9312. ...
  9313. /* parameter wrapup section */
  9314. #undef max_TYPE
  9315. #undef stack_ITEM
  9316. #under stack_MAX_ITEMS
  9317. #under stack
  9318.  
  9319.  
  9320. Rules for Using Templates
  9321.  
  9322.  
  9323. 1) In order to use a form template to generate either declarations or
  9324. definitions, you must define its name. For a C++ function form template you
  9325. define its first parameter instead. For example:
  9326. #define max_ITEM int
  9327. ...
  9328. #define stack stack_strings
  9329. The above code snippet indicates we are about to generate the function max for
  9330. integers and a parameterized class stack named stack_strings.
  9331. 2) All of the various form template parameters must also be defined as
  9332. required. For example:
  9333. #define max_TYPE int
  9334. #define stack_ITEM char
  9335. 3) Immediately following the form template envelope names and parameters,
  9336. include the declarations generating file. To generate both declarations and
  9337. definitions, include the definitions generating file instead, as in:
  9338. #define max_TYPE int
  9339.  
  9340. #define stack_ITEM char
  9341. #define stack stack_strings
  9342. #include "def_gen.cpp"
  9343. At this point we have generated declarations and definitions for:
  9344. int max(int x, int y);
  9345. and
  9346. stack_strings; // i.e. stack<char, 5U>
  9347. with all form template envelope names and parameters left undefined!
  9348. 4) To generate an additional type from the same form template, simply repeat
  9349. the process.
  9350. define stack_ITEM int
  9351. #define stack_MAX_ITEMS 10U
  9352. #define stack stack_int_10U
  9353. #include "def_gen.cpp"
  9354. At this point we have also generated a declaration and definition for:
  9355. stack_int_10U; // i.e. stack<int, 1OU>
  9356. The naming conventions used for these stacks has no significance as far as the
  9357. form template generating mechanism is concerned.
  9358.  
  9359.  
  9360. Synthesizing Templates in C
  9361.  
  9362.  
  9363. Listing 7, Listing 8, and Listing 9 repeat our example, this time in C. Please
  9364. note the following differences from the C++ approach:
  9365. 1) Function form template envelopes have the same name as the function itself:
  9366. /* max function envelope (see Listing 7) */
  9367. #ifdef max/* C uses function name test */
  9368. extern max_TYPE max(max_TYPE x, max_TYPE y);
  9369. #endif
  9370. Since C doesn't allow for inlined functions, max is declared as external in
  9371. the header file and defined in the source file as:
  9372. /* max function envelope (see Listing 8) */
  9373. #ifdef max /* C uses function name test */
  9374. max_TYPE max(max_TYPE x, max_TYPE y)
  9375. { return (x > y) ? x : y; }
  9376. #endif
  9377. 2) Neither does C allow for function name overloading, so in order to generate
  9378. a max function we must redefine its name as well as its parameter.
  9379. #define max max_iii /* see Listing 9 */
  9380. #define max_TYPE int
  9381. ...
  9382. #include "def_gen.c"
  9383. The convention I use here, max_iii, indicates a function generically named max
  9384. returning an integer and taking two integer parameters. In order for C++ to
  9385. allow for function-name overloading, the C++ translator generates unique
  9386. mangled names based on return and parameter types. You can think of max_iii as
  9387. a hand-mangled name. You might name a function for floats max_fff. However,
  9388. you must call the proper function, max_iii or max_fff, unlike in C++.
  9389. 3) Since C doesn't support member functions, you must define a scoping macro
  9390. to support pseudo membership, as in:
  9391. #define stack_ITEM char /* see
  9392. Listing 9 */
  9393. #define stack stack_char
  9394. #define stack_scope(memFnc)
  9395. stack_char_ ## memFnc
  9396. #include "def_gen.c"
  9397. The macro stack_scope is used in both the form template header and source
  9398. files, as in:
  9399. extern int stack_scope(push) /* see
  9400. Listing 7 */
  9401. (struct stack * stackPtr,
  9402. stack_ITEM * itemPtr);
  9403.  
  9404. int stack_scope(push) /* see Listing
  9405. 8 */
  9406. (struct stack * stackPtr, stack_ITEM * itemPtr)
  9407. {...}
  9408. to generate stack-scoped function names. You can think of this as a different
  9409. sort of name mangling. The function name is mangled to reflect its pseudo
  9410. structure membership instead of its variant parameter types. In order to call
  9411. a pseudo member function you must specify its scope mangled name, as in:
  9412. /* see Listing 9 */
  9413. stack_char_push(&ToDo,"wash car");
  9414.  
  9415. The struct scoping macro must be undefined in the parameter wrapup sections.
  9416. See the tail of Listing 7 and Listing 8.
  9417.  
  9418.  
  9419. Conclusion
  9420.  
  9421.  
  9422. The "form template" approach shown here can be used in a nested fashion to
  9423. implement any structure that can be described with conventional C++ templates.
  9424. No longer must you limit your designs to non-parameterized types simply
  9425. because your C++ translator doesn't yet support templates. Likewise C
  9426. application design can now also benefit from the C++ template concept.
  9427.  
  9428. Listing 1 Implementing a stack of strings in C
  9429. struct StrStack {
  9430. char * itemPtrs[5U];
  9431. unsigned items;
  9432. };
  9433.  
  9434. void init(struct StrStack * stackPtr)
  9435. {
  9436. if (stackPtr)
  9437. stackPtr->items = 0U;
  9438. }
  9439.  
  9440. int full(struct StrStack * stackPtr)
  9441. {
  9442. if (stackPtr && stackPtr->items < 5U)
  9443. return 0;
  9444. return 1;
  9445. }
  9446.  
  9447. int push(struct StrStack * stackPtr, char * itemPtr)
  9448. {
  9449. if (!full(stackPtr) && itemPtr) {
  9450. stackPtr->itemPtrs[stackPtr->items++]
  9451. = itemPtr;
  9452. return 1;
  9453. }
  9454. return 0;
  9455. }
  9456.  
  9457. char * top(struct StrStack * stackPtr)
  9458. {
  9459. if (stackPtr && stackPtr->items)
  9460. return stackPtr->itemPtrs[stackPtr->items-1];
  9461. return (char *) 0;
  9462. }
  9463.  
  9464. char * pop(struct StrStack * stackPtr)
  9465. {
  9466. if (stackPtr && stackPtr->items)
  9467. return stackPtr->itemPtrs[--stackPtr->items];
  9468. return (char *) 0;
  9469. }
  9470.  
  9471. #include <stdio.h>
  9472.  
  9473. main()
  9474. {
  9475. struct StrStack ToDo;
  9476.  
  9477. init(&ToDo);
  9478. (void) push(&ToDo,"wash car");
  9479.  
  9480. (void) push(&ToDo,"cut grass");
  9481. (void) push(&ToDo,"buy groceries");
  9482. (void) push(&ToDo,"cash paycheck");
  9483. while (top(&ToDo))
  9484. (void) printf("%s\n",pop(&ToDo));
  9485. return 0;
  9486. }
  9487. /* End of File */
  9488.  
  9489.  
  9490. Listing 2 The C++ version of Listing 1
  9491. class StrStack {
  9492. char * itemPtrs[5U];
  9493. unsigned items;
  9494. public:
  9495. StrStack() { items = 0U; }
  9496. int full() { return !(5U - items); }
  9497. unsigned depth() { return items; }
  9498. int push(char * itemPtr);
  9499. char * top()
  9500. { return (items? itemPtrs[items-1] : 0); }
  9501. char * pop()
  9502. { return (items? itemPtrs[--items] : 0); }
  9503. };
  9504.  
  9505. int StrStack::push(char * itemPtr)
  9506. {
  9507. if (!full() && itemPtr) {
  9508. itemPtrs[items++] = itemPtr;
  9509. return 1;
  9510. }
  9511. return 0;
  9512. }
  9513.  
  9514. #include <iostream.h>
  9515. #include <iomanip.h>
  9516.  
  9517. main()
  9518. {
  9519. StrStack ToDo;
  9520.  
  9521. ToDo.push("wash car");
  9522. ToDo.push("cut grass");
  9523. ToDo.push("buy groceries");
  9524. ToDo.push("cash paycheck");
  9525. while (ToDo.top())
  9526. cout << ToDo.pop() << endl;
  9527. return 0;
  9528. }
  9529. /* End of File */
  9530.  
  9531.  
  9532. Listing 3 Using a class template to parameterize the stack class
  9533. template <class TYPE>
  9534. inline TYPE max(TYPE x, TYPE y)
  9535. { return (x > y) ? x : y; }
  9536.  
  9537. template <class ITEM, unsigned MAX_ITEMS = 5>
  9538. class stack {
  9539.  
  9540. ITEM * itemPtrs[MAX_ITEMS];
  9541. unsigned items;
  9542. public:
  9543. stack() ( items = 0U; }
  9544. int full() { return !(MAX_ITEMS - items); }
  9545. unsigned depth() { return items; }
  9546. int push(ITEM * itemPtr);
  9547. ITEM * top()
  9548. { return (items? itemPtrs[items-1] : 0); }
  9549. ITEM * pop()
  9550. { return (items? itemPtrs[--items] : 0); }
  9551. };
  9552.  
  9553. template <class ITEM, unsigned MAX_ITEMS>
  9554. int stack<ITEM,MAX_ITEMS>::push(ITEM * itemPtr)
  9555. {
  9556. if (!full() && itemPtr) {
  9557. itemPtrs[items++] = itemPtr;
  9558. return 1;
  9559. }
  9560. return 0;
  9561. }
  9562.  
  9563. #include <iostream.h>
  9564. #include <iomanip.h>
  9565.  
  9566. main()
  9567. {
  9568. stack<char> ToDo;
  9569.  
  9570. ToDo.push("wash car");
  9571. ToDo.push("cut grass");
  9572. ToDo.push("buy groceries");
  9573. ToDo.push("cash paycheck");
  9574. while (ToDo.top())
  9575. cout << ToDo.pop() << endl;
  9576.  
  9577. stack<int,10U> CountDown;
  9578.  
  9579. for (int i = 1; CountDown.push(new int(i)); i++);
  9580. while (CountDown.top()) {
  9581. cout << *CountDown.top() << " ";
  9582. delete CountDown.pop();
  9583. }
  9584. cout << "Blast Off!" << endl;
  9585. return 0;
  9586. }
  9587. /* End of File */
  9588.  
  9589.  
  9590. Listing 4 Declaring synthesized templates in C++
  9591. /* decl_gen.hpp -- declarations generating file */
  9592.  
  9593. /* max function envelope */
  9594. #ifdef max_TYPE /* C++ uses first parameter test */
  9595. inline max_TYPE max(max_TYPE x, max_TYPE y)
  9596. { return (x > y) ? x : y; }
  9597. #endif
  9598.  
  9599.  
  9600. #ifdef stack /* open stack class envelope */
  9601.  
  9602. #ifndef stack_MAX_ITEMS /* default parameters */
  9603. #define stack_MAX_ITEMS 5U
  9604. #endif
  9605.  
  9606. class stack {
  9607. stack_ITEM * itemPtrs[stack_MAX_ITEMS];
  9608. unsigned items;
  9609. public:
  9610. stack() { items = 0U; }
  9611. int full() { return !(stack_MAX_ITEMS - items); }
  9612. unsigned depth() { return items; }
  9613. int push(stack_ITEM * itemPtr);
  9614. stack_ITEM * top()
  9615. { return (items? itemPtrs[items-1] : 0); }
  9616. stack_ITEM * pop()
  9617. { return (items? itemPtrs[--items] : 0); }
  9618. };
  9619.  
  9620. #endif /* close stack class envelope */
  9621.  
  9622. #ifndef def_gen_cpp /* parameter wrapup section */
  9623. #undef max_TYPE
  9624. #undef stack
  9625. #undef stack_ITEM
  9626. #undef stack_MAX_ITEMS
  9627. #endif
  9628. // End of File
  9629.  
  9630.  
  9631. Listing 5 Defining synthesized templates in C++
  9632. /* def_gen.cpp -- definitions generating file */
  9633.  
  9634. #define def_gen_cpp
  9635. #include "decl_gen.hpp"
  9636. #undef def_gen_cpp
  9637.  
  9638. #ifdef stack /* open stack class envelope */
  9639.  
  9640. int stack::push(stack_ITEM * itemPtr)
  9641. {
  9642. if (!full() && itemPtr) {
  9643. itemPtrs[items++] = itemPtr:
  9644. return 1;
  9645. }
  9646. return 0;
  9647. }
  9648.  
  9649. #endif /* close stack class envelope */
  9650.  
  9651. /* parameter wrapup section */
  9652. #undef max_TYPE
  9653. #undef stack
  9654. #undef stack_ITEM
  9655. #undef stack_MAX_ITEMS
  9656.  
  9657. // End of File
  9658.  
  9659.  
  9660.  
  9661. Listing 6 Using synthesized templates in C++
  9662. /* formtemp.cpp -- use form templates */
  9663.  
  9664. /*
  9665. Generate declarations and definitions for
  9666.  
  9667. int max(int,int);
  9668. stack<char>
  9669. /*
  9670. #define max_TYPE int
  9671. #define stack_ITEM char
  9672. #define stack stack_strings
  9673. #include "def_gen.cpp"
  9674.  
  9675. /*
  9676. Generate declarations and definitions for
  9677.  
  9678. stack<int,10U>
  9679. */
  9680. #define stack_ITEM int
  9681. #define stack_MAX_ITEMS 10U
  9682. #define stack stack_int_10U
  9683. #include "def_gen.cpp"
  9684.  
  9685. #include <iostream.h>
  9686. #include <iomanip.h>
  9687.  
  9688. main()
  9689. {
  9690.  
  9691. cout << "Which is greater? "
  9692. << 4 <<"or"<<"5<<"?";
  9693. cout <<" Answer: "
  9694. << max(4,5) << "!" << endl;
  9695.  
  9696. stack_strings ToDo;
  9697.  
  9698. ToDo.push("wash car");
  9699. ToDo.push("cut grass");
  9700. ToDo.push("buy groceries");
  9701. ToDo.push("cash paycheck");
  9702. while (ToDo.top())
  9703. cout << ToDo.pop() << endl;
  9704.  
  9705. stack_int_10U CountDown;
  9706.  
  9707. for (int i = 1; CountDown.push(new int(i)); i++);
  9708. while (CountDown.top()) {
  9709. cout << *CountDown.top() << " ";
  9710. delete CountDown.pop();
  9711. }
  9712. cout << "Blast Off!" << endl;
  9713. return 0;
  9714. }
  9715. // End of File
  9716.  
  9717.  
  9718. Listing 7 Declaring synthesized templates in C
  9719.  
  9720. /* decl_gen.h -- declarations generating file */
  9721.  
  9722. /* max function envelope */
  9723. #ifdef max /* C uses function name test */
  9724. extern max_TYPE max(max_TYPE x, max_TYPE y);
  9725. #endif
  9726.  
  9727. #ifdef stack /* open stack struct envelope */
  9728. #ifdef stack_MAX_ITEMS /* default parameters */
  9729. #define stack_MAX_ITEMS 5U
  9730. #endif
  9731.  
  9732. struct stack {
  9733. stack_ITEM * itemPtrs[stack_MAX_ITEMS];
  9734. unsigned items;
  9735. };
  9736.  
  9737. extern void stack_scope(init)
  9738. (struct stack * stackPtr);
  9739. extern int stack_scope(full)
  9740. (struct stack * stackPtr);
  9741. extern int stack_scope(push)
  9742. (struct stack * stackPtr, stack_ITEM * itemPtr);
  9743. extern stack_ITEM * stack_scope(top)
  9744. (struct stack * stackPtr);
  9745. extern stack_ITEM * stacks_cope(pop)
  9746. (struct stack * stackPtr);
  9747.  
  9748. #endif /* close stack struct envelope */
  9749.  
  9750. #ifndef def_gen_c /* parameter wrapup section */
  9751. #undef max
  9752. #undef max_TYPE
  9753. #under stack
  9754. #undef stack_ITEM
  9755. #undef stack_MAX_ITEMS
  9756. #undef stack_scope
  9757. #endif
  9758. /* End of File */
  9759.  
  9760.  
  9761. Listing 8 Defining synthesized templates in C
  9762. / * def_gen.c -- definitions generating file * /
  9763.  
  9764. #define def_gen_c
  9765. #include "decl_gen.h"
  9766. #undef def_gen_c
  9767.  
  9768. /* max function envelope */
  9769. #ifdef max /* C uses function name test */
  9770. max_TYPE max(max_TYPE x, max_TYPE y)
  9771. { return (x > y) ? x: y; }
  9772. #endif
  9773.  
  9774. #ifdef stack /* open stack struct envelope */
  9775.  
  9776. void stack_scope(init)(struct stack * stackPtr)
  9777. {
  9778. if (stackPtr)
  9779.  
  9780. stackPtr->items = 0U;
  9781. }
  9782.  
  9783. int stack_scope(full)(struct stack * stackPtr)
  9784. {
  9785. if (stackPtr && stackPtr->items < stack_MAX_ITEMS)
  9786. return 0;
  9787. return 1;
  9788. }
  9789.  
  9790. int stack_scope(push)
  9791. (struct stack * stackPtr, stack_ITEM * itemPtr)
  9792. {
  9793. if (!stack_scope(full)(stackPtr) && itemPtr) {
  9794. stackPtr->itemPtrs[stackPtr->items++]
  9795. = itemPtr;
  9796. return 1;
  9797. }
  9798. return 0;
  9799. }
  9800.  
  9801. stack_ITEM * stack_scope(top)(struct stack * stackPtr)
  9802. {
  9803. if (stackPtr && stackPtr->items)
  9804. return stackPtr->itemPtrs[stackPtr->items-1];
  9805. return (stack_ITEM *) 0;
  9806. }
  9807.  
  9808. stack_ITEM * stack_scope(pop)(struct stack * stackPtr)
  9809. {
  9810. if (stackPtr && stackPtr->items)
  9811. return stackPtr->itemPtrs[--stackPtr->items];
  9812. return (stack_ITEM *) 0;
  9813. }
  9814.  
  9815. #endif /* close stack struct envelope */
  9816.  
  9817. #undef max
  9818. #undef max_TYPE
  9819. #under stack
  9820. #undef stack_ITEM
  9821. #undef stack_MAX_ITEMS
  9822. #under stack_scope
  9823. /* End of File */
  9824.  
  9825.  
  9826. Listing 9 Using synthesized templates in C
  9827. /* formtemp.c -- use form templates */
  9828.  
  9829. /*
  9830. Generate declarations and definitions for
  9831.  
  9832. int max(int,int);
  9833. stack<char>
  9834. */
  9835. #define max max_iii
  9836. #define max_TYPE int
  9837. #define stack_ITEM char
  9838. #define stack stack_char
  9839.  
  9840. #define stack_scope(memFnc) stack_char_ ## memFnc
  9841. #include "def_gen.c"
  9842.  
  9843. /*
  9844. Generate declarations and definitions for
  9845.  
  9846. stack<int,10U>
  9847. */
  9848. #define stack_ITEM int
  9849. #define stack MAX_ITEMS 10U
  9850. #define stack stack_int_10U
  9851. #define stack_scope(memFnc) stack_int_10U_ ## memFnc
  9852. #include "def_gen.c"
  9853.  
  9854. #include <stdio.h>
  9855. #include <stdlib.h>
  9856.  
  9857. main()
  9858. {
  9859. struct stack_char ToDo;
  9860. struct stack_int_10U CountDown;
  9861. int i, * iptr;
  9862.  
  9863. (void) printf("Which is greater? %d or %d?"
  9864. "Answer: %d!\n",4,5,max_iii(4,5));
  9865.  
  9866. stack_char_init(&ToDo);
  9867. stack_char_push(&ToDo,"wash car");
  9868. stack_char_push(&ToDo,"cut grass");
  9869. stack_char_push(&ToDo,"buy groceries");
  9870. stack_char_push(&ToDo,"cash paycheck");
  9871. while (stack_char_top(&ToDo))
  9872. (void) printf("%s\n",stack_char_pop(&ToDo));
  9873.  
  9874. stack_int_10U_init(&CountDown);
  9875. for (i = 1; !stack_int_10U_full(&CountDown); i++)
  9876. {
  9877. iptr = (int *) malloc(sizeof(int));
  9878. if (!iptr)
  9879. break;
  9880. * iptr = i;
  9881. (void) stack_int_10U_push(&CountDown,iptr);
  9882. }
  9883. while (stack_int_10U_top(&CountDown)) {
  9884. (void) printf("%d ",
  9885. * stack_int_10U_top(&CountDown));
  9886. free(stack_int_10U_pop(&CountDown));
  9887. }
  9888. (void) printf("Blast Off!\n");
  9889. return 0;
  9890. }
  9891. /* End of File */
  9892.  
  9893.  
  9894.  
  9895.  
  9896.  
  9897.  
  9898.  
  9899.  
  9900.  
  9901.  
  9902.  
  9903.  
  9904.  
  9905.  
  9906.  
  9907.  
  9908.  
  9909.  
  9910.  
  9911.  
  9912.  
  9913.  
  9914.  
  9915.  
  9916.  
  9917.  
  9918.  
  9919.  
  9920.  
  9921.  
  9922.  
  9923.  
  9924.  
  9925.  
  9926.  
  9927.  
  9928.  
  9929.  
  9930.  
  9931.  
  9932.  
  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. EMS Professional Shareware Libraries -- Utilities for C/C++
  9964.  
  9965.  
  9966. Bob Swart
  9967.  
  9968.  
  9969. Bob Swart is a professional software developer and free-lance technical author
  9970. using Borland Pascal, C++, and Delphi. In his spare time he likes to watch
  9971. video tapes of Star Trek The Next Generation with his 9-month old son Erik
  9972. Mark Pascal.
  9973.  
  9974.  
  9975. EMS Professional Shareware Libraries are a comprehensive collection of public
  9976. domain, shareware, and free products supporting PC professionals in many
  9977. speciality areas like Pascal, ASM, Windows, (Visual) Basic, Spreadsheets,
  9978. Databases (Access, dBase, Clipper, FoxPro), and of course C/C++.
  9979. Each library consists of a large collection of .ZIP archives (more than 1,600+
  9980. different files in the Utility Library for C/C++, on 99 (!) 1.44 MB diskettes
  9981. or one CD-ROM). The C/C++ Utility Directory is the result of hundreds of hours
  9982. of data collection, checking, and preparation by C experts Mark Harris and
  9983. Bill Gerrard.
  9984. Typical librarians, such as Harris and Gerrard, are specialists with a strong
  9985. interest in collecting and organizing information relating to their field (in
  9986. this case, C and C++). Librarians are typically BBS addicts who like to spend
  9987. lots of hours calling local and long distance BBSs, online services (such as
  9988. CompuServe, AOL, GEnie), and FPT sites on the Internet. They download
  9989. everything relating to their subject area and write descriptions into a
  9990. database EMS provides. The librarians also enter address, phone, and e-mail
  9991. information, which EMS then uses to contact the authors for more recent
  9992. versions, description corrections, etc.
  9993. For most of EMS's collections (including the C/C++ Library) the librarian will
  9994. also enter information on all related commercial products they discover in
  9995. magazine articles and advertisements. EMS does not place these commercial
  9996. products on the CD-ROM, but they do maintain contact information and
  9997. descriptions in the database (included on the CD-ROM, also available
  9998. separately if you buy the floppies) so that customers have "one-stop shopping"
  9999. for virtually every C/C++ product that might solve the problem they face that
  10000. day.
  10001. The EMS Library contains programs and complete source code, under the
  10002. following categories: Arrays, Benchmark, Binary Tree, Bit Manipulation, AI,
  10003. Bugs, Code Analysis, Communication, Compiler, Container & BIDS, Database,
  10004. Date/Time, Debug, DOS, Editor, Graphics, Keyboard, Linked List, Mathematics,
  10005. Memory Mgmt., MFC, Mouse, Multitask, Network, NT, OS/2, OWL, Printer, Screen,
  10006. Sound, Streams, String, Template, Turbo Vision, User Interface, Virtual
  10007. Object, and Windows.
  10008. But believe me, this library's sheer size would make describing what you could
  10009. find almost as difficult as finding something in it -- if it weren't for the
  10010. additional database directory EMS supplies for each library. The current
  10011. database directory consists of a single 1.2 MB dBASE III+ compatible .DBF
  10012. file. You can view this file with dBase, or use the supplied program from EMS.
  10013. CUTIL, for use with the C/C++ library, allows you to search for products by
  10014. vendor, type, product name, or free-text search, and will let you view the
  10015. contents of the actual ZIP file from within the database index program.
  10016. Whenever you have one or more hits, you get the product name, file name,
  10017. release date, size, producer, address (including e-mail), phone/fax numbers,
  10018. and description.
  10019. The Library also includes a README file that contains the names of several
  10020. dedicated C/C++ BBSs, book publishers, and publications (including C/C++ Users
  10021. Journal and Windows/DOS Developer's Journal).
  10022. The Directory and Library are updated six times per year. Previous purchasers
  10023. are eligible for a 25 percent reduction from the current list price, which, as
  10024. of this writing, is $25.00 for the directory and $149.00 for the library on 99
  10025. diskettes, or $59.50 for directory plus library on CD-ROM (with many
  10026. additional products).
  10027. EMS also sells a C++ (only) version of the library, which consists of just 772
  10028. files on 53 diskettes for $99.50. The $59.50 CD-ROM contains the full C/C++
  10029. version, so you'd better buy a CD-ROM player now!
  10030. Product Information
  10031. Title: Utility Library for C/C++
  10032. Librarians: Mark Harris and Bill Gerrard
  10033. Files: more than 1,600
  10034. Price: $59.50 (CD-ROM), $149 (99 diskettes)
  10035. Publisher: EMS Professional Shareware
  10036. 4505 Buckhurst Court
  10037. Olney, MD 20832-1830, USA
  10038. +1 301 924-3594, FAX: +1 301 963-2708
  10039. e-mail: eengelmann@worldbank.org
  10040. Web: http://www.paltech.com/ems/ems.htm
  10041.  
  10042.  
  10043.  
  10044.  
  10045.  
  10046.  
  10047.  
  10048.  
  10049.  
  10050.  
  10051.  
  10052.  
  10053.  
  10054.  
  10055.  
  10056.  
  10057.  
  10058.  
  10059.  
  10060.  
  10061.  
  10062.  
  10063.  
  10064.  
  10065.  
  10066.  
  10067.  
  10068.  
  10069.  
  10070.  
  10071.  
  10072.  
  10073. Standard C/C++
  10074.  
  10075.  
  10076. Implementing <strstream>
  10077.  
  10078.  
  10079.  
  10080.  
  10081. P.J. Plauger
  10082.  
  10083.  
  10084. P.J. Plauger is senior editor of C/C++ Users Journal. He is convener of the
  10085. ISO C standards committee, WG14, and active, on the C++ Committee, WG21. His
  10086. latest books are The Draft Standard C++ Library, and Programming on Purpose
  10087. (three volumes), all published by Prentice-Hall. You can reach him at
  10088. pjp@plauger.com.
  10089.  
  10090.  
  10091.  
  10092.  
  10093. Introduction
  10094.  
  10095.  
  10096. I introduced the header <strstream> last month and showed a variety of ways to
  10097. use the classes it defines. (See "Standard C/C++: The Header <strstream>,"
  10098. CUJ, January 1995.) Among other things, it defines the class istrstream, which
  10099. is derived from istream to help you extract from a character sequence stored
  10100. in memory. Thus, you can write code like:
  10101. istrstream strin("1 4 7 2 5 8 0
  10102. 3 6 9");
  10103. int i;
  10104. while (strin >> i)
  10105. try_button(i);
  10106. to "read" a constant string just as if it were a file.
  10107. Similarly, class ostrstream is derived from ostream to help you insert into a
  10108. character sequence stored in memory. You can construct a string as if writing
  10109. to a file, for example, then read it later with an istrstream object
  10110. controlling the same stream buffer, as above. For the special magic involved,
  10111. both make use of the stream buffer class strstreambuf. As with all stream
  10112. buffers, it is derived from streambuf, in this case to manage such in-memory
  10113. character sequences. (See "Standard C/C++: The Header <streambuf>," CUJ, June
  10114. 1994.)
  10115. The net effect is that the classes defined in <strstream> let you read and
  10116. write these character sequences with exactly the same machinery you use to
  10117. read and write external files in C++. You can, of course, perform much the
  10118. same operations with the Standard C library functions sprintf and sscanf,
  10119. declared in <stdio.h>. But those older functions require you to use different
  10120. notation for reading and writing in-memory character sequences rather than
  10121. external files. And they don't work with the inserter and extractor machinery
  10122. of C++. The classes defined in <strstream> offer an obvious notational
  10123. advantage, as well as better information hiding.
  10124. My goal this month is to show you one way to implement these classes. It is
  10125. part of the implementation I have been presenting for the past year, which is
  10126. based on the draft C++ Standard going into the March 1994 meeting of
  10127. WG21/X3J16. That in turn was based heavily on the existing header
  10128. <strstream.h> which is still widely used as part of the iostreams package of
  10129. library classes. If you know current C++ practice, much of what you see here
  10130. will be familiar territory.
  10131. In more recent drafts of the C++ Standard, many headers have been
  10132. "templatized." The Standard C++ library now defines general templates that
  10133. describe iostreams operations for an arbitrary "character" type T. To
  10134. reproduce the existing functionality, the library instantiates these general
  10135. templates for T defined as type char. The net result is much the same, but the
  10136. machinery -- and the notation -- is now far more elaborate.
  10137. The header <strstream> has most recently been exempted from this treatment.
  10138. The committee favors its newly minted header <sstream>, which does much the
  10139. same thing as <strstream> but works with templatized strings of arbitrary
  10140. character types. (I discuss the char version of the newer header next month.)
  10141. The older header is retained, in the interest of preserving existing code, but
  10142. as a sort of second-class citizen. For all its idiosyncracies, I personally
  10143. find it a handy way to fiddle with small sequences of type char. So what you
  10144. see here is still quite useful. And it is still destined to be part of the
  10145. final Standard C++ library.
  10146.  
  10147.  
  10148. The Header File
  10149.  
  10150.  
  10151. Listing 1 shows the header file that implements strstream. I've discussed some
  10152. of the peculiar notation in past columns, but I've also made a few changes as
  10153. compilers evolve. Briefly:
  10154. The macro _BITMASK defines a "bitmask" type. (See "Standard C/C++: The Header
  10155. <ios>," CUJ, May 1994.) It expands differently depending on whether the
  10156. translator supports overloading on enumerated types, a fairly recent addition
  10157. to the C++ language. I later discovered a need to defer the definitions of the
  10158. overloaded functions, for bitmask types nested inside classes as is the case
  10159. here. Thus, the macro _BITMASK_OPS supplies these deferred definitions.
  10160. The type bool has even more recently been added to the C++ language. It
  10161. represents the true/false value of a test expression, such as a comparison
  10162. operator. Until recently, I provided the typedef _Bool as a placeholder. But
  10163. since some translators now supply this type, I've brought my code more up to
  10164. date. (For older translators, bool is a defined type, not a keyword.)
  10165. The macro _HAS_SIGNED_CHAR expands to a nonzero value for translators that
  10166. treat char and signed char as distinct types. All translators are supposed to,
  10167. but many still do not.
  10168. I have added to the header two macros to specify in one place two values that
  10169. are judgement calls. Both deal with the number of characters to allocate when
  10170. creating or extending a character sequence:
  10171. _ALSIZE, the initial number of bytes to allocate, absent any hints to the
  10172. contrary (currently 512)
  10173. _MlNSIZE, the minimum number of additional bytes to allocate when extending a
  10174. sequence (currently 32)
  10175. You may well have reasons to alter either or both of these values, based on
  10176. what you know about storage size and granularity on a given implementation.
  10177. The bitmask type _Strstate, defined within the class strstreambuf, describes
  10178. the internal state of such an object. Much of the state information is spelled
  10179. out in detail by the draft C++ Standard. I have added, however, the element
  10180. _Noread, which is not used by the classes defined in <strstream>. Adding it
  10181. here greatly simplifies the implementation of the classes defined in <sstream>
  10182. (next month). The meaning of each of the _Strstate elements is:
  10183. _Allocated, set when the character sequence has been allocated
  10184. _Constant, set when the character sequence is not to permit insertions
  10185. _Dynamic, set when the character sequence can grow on demand
  10186. _Frozen, set when the character sequence has been frozen (should not be
  10187. deleted by the destructor)
  10188. _Noread, set when the character sequence is not to permit extractions (is
  10189. write only)
  10190. I developed the protected secret member function strstreambuf::_Init as a way
  10191. to handle all possible constructors, including those for class stringbuf,
  10192. defined in the header <sstream>. Similarly, the protected secret member
  10193. function strstreambuf::_Tidy does all the work of the destructor. It is also
  10194. used to advantage in class stringbuf. (See the discussion of Listing 2,
  10195. below.)
  10196. Most of the objects stored within a strstreambuf object are what you might
  10197. expect. _Strmode holds the state information. Alsize holds the current
  10198. allocated sequence length. _Palloc and _Pfree point at the functions that
  10199. allocate and free storage, if they are specified when the object is
  10200. constructed.
  10201. But there are also two additional private member objects. Each solves a
  10202. different problem in managing accesses to the controlled character sequence.
  10203. _Penadsave stores the end pointer for the output sequence while the stream
  10204. buffer is frozen. (See the discussion of Listing 4, below.) _Seekhigh stores
  10205. the highest defined offset encountered so far within the character sequence.
  10206. The code updates its stored value in several places when that value must be
  10207. made exact.
  10208.  
  10209.  
  10210. Workhorse Functions
  10211.  
  10212.  
  10213.  
  10214. Listing 2 shows the file strstrea.c. It defines three of the functions you are
  10215. likely to need any time you declare an object of class strstreambuf -- its
  10216. destructor, _Init, and _Tidy. Two of the three functions are straightforward,
  10217. but _Init warrants a bit of study. It selects among multiple forms of
  10218. initialization by an intricate analysis of its arguments:
  10219. If gp (the "get" pointer) is a null pointer, then n (the size argument) is a
  10220. suggested initial allocation size.
  10221. Otherwise, if mode has the bit _Dynamic set, then the initial character
  10222. sequence is copied from one controlled by a string object. The function copies
  10223. n characters beginning at gp. The calling string constructor can independently
  10224. inhibit insertions (_Constant) and/or extractions (_Norend).
  10225. Otherwise, the character sequence resides in an existing character array
  10226. beginning at gp. If n is less than zero, the sequence is assumed to be
  10227. arbitrarily large (INT_MAX characters). If n is zero, the array is assumed to
  10228. contain a null-terminated string, which defines the character sequence. If n
  10229. is greater than zero, it is taken as the length of the character sequence. The
  10230. function defines an output stream only if pp (the "put" pointer) is not a null
  10231. pointer and lies within the character sequence.
  10232. Be warned that this code is extremely fragile. Partly, it reflects the
  10233. complexities of the numerous strstreambuf constructors (which I described last
  10234. month). Partly, it is made larger by the inclusion of support for stringbuf
  10235. constructors. But the code also enforces delicate streambuf semantics that are
  10236. hard to spell out in detail. Tinker cautiously.
  10237. Listing 3 shows the file strstpro.c. It defines three functions that override
  10238. streambuf virtual member functions to insert and extract characters --
  10239. overflow, pbackfail, and underflow. The inherited definition of uflow is
  10240. adequate, so no override occurs here. (See "Standard C/C++: The Header
  10241. <streambuf>," CUJ, June 1994.) Once again, two of the three functions are
  10242. straightforward. Only overflow demands closer study.
  10243. It is the business of overflow to "make a write position available," then
  10244. insert the argument character into it. If the write position is already
  10245. available, or if none can be made available, the function has an easy job of
  10246. it. The hard part comes when the function must extend, or initially create,
  10247. storage for the character sequence. It must then determine the size of any
  10248. existing sequence (osize) and the desired new size (nsize). Then it can try to
  10249. allocate the new storage, copy over any existing sequence, and free an
  10250. existing sequence that was also allocated. Finally, it must determine new
  10251. settings for the streambuf pointers, using some very finicky arithmetic.
  10252.  
  10253.  
  10254. Other Functions
  10255.  
  10256.  
  10257. Listing 4 shows the file strstfre.c, which defines the member function
  10258. strstreambuf::freeze. Here is where the addition of the member object
  10259. strstreambuf::_Pendsave saves the day. A frozen buffer must not permit
  10260. insertions, but that is not an easy thing to prevent. The streambuf public
  10261. member functions won't look past the pointers themselves if they indicate that
  10262. a write position is available. So the trick is to make the output stream
  10263. appear empty for a frozen stream buffer by jiggering the end pointer.
  10264. _Pendsave stores the proper value for later restoration, should the stream
  10265. buffer be unfrozen.
  10266. Listing 5 shows the file strstpos.c. It defines the two functions that
  10267. override streambuf virtual member functions to alter the stream position --
  10268. seekoff and seekpos. The often critical value in both functions is the member
  10269. object strstrambuf::_Seekhigh. It is updated as needed to reflect the current
  10270. "end," or high-water mark, of the character sequence. That value determines
  10271. offsets relative to the end (way equals ios::end), as well as an upper bound
  10272. for valid stream offsets. The logic of both functions is otherwise simple but
  10273. tedious.
  10274. And that concludes the source code for class strstreambuf. The two remaining
  10275. classes defined in <strstream> are derived from the classes istream and
  10276. ostream to assist in controlling inmemory character streams. I described how
  10277. to use both istrstream and ostrstream last month. As you can see from Listing
  10278. 1, most of the member functions are small and hence defined as inline.
  10279. Listing 6 shows the file istrstre.c. It defines the destructor for class
  10280. istrstream, which is the only member function not defined inline within the
  10281. class. And Listing 7 shows the file ostrstre.c. It defines the destructor, and
  10282. a moderately messy constructor, for class ostrstream. I put the constructor
  10283. here mostly to hide the call to the function strlen, declared in <string.h>.
  10284. It is not permissible to include the C header that declares it in <strstream>
  10285. and I didn't want to make up a version of the function with a secret name.
  10286. Again this is the only source code for member functions of class ostrstream
  10287. not defined inline within the class.
  10288. While there are a few tricky spots in the implementation of the stream buffer,
  10289. most of the code that implements the header <strstream> is small and
  10290. straightforward. You will find much the same story when we visit other
  10291. specialized stream buffers derived from class streambuf and its brethren. It
  10292. is a tribute to the basic design of iostreams that this is so.
  10293. This article is excerpted in part from P.J. Plauger, The Draft Standard C++
  10294. Library, (Englewood Cliffs, N.J.: Prentice-Hall, 1995).
  10295.  
  10296. Listing 1 The header <strstream>
  10297. // strstream standard header
  10298. #ifndef _STRSTREAM_____LINEEND____
  10299. #define _STRSTREAM_____LINEEND____
  10300. #include <istream>
  10301. #include <ostream>
  10302. // constants
  10303. const int _ALSIZE = 512; // default allocation size
  10304. const int _MINSIZE = 32; // minimum allocation size
  10305. // class strstreambuf
  10306. class strstreambuf = public streambuf {
  10307. public:
  10308. enum __Strstate {_Allocated = 1, _Constant = 2,
  10309. _Dynamic = 4, _Frozen = 8, _Noread = 16,
  10310. _Strzero = 0};
  10311. _BITMASK(_Strstate, _Strstate);
  10312. strstreambuf(streamsize _N = 0)
  10313. {_Init(_N); }
  10314. strstreambuf(void *(*_A)(size_t), void (*_F)(void *))
  10315. {_Init(), _Palloc = _A,_Pfree =_F; }
  10316. strstreambuf(char *_G, streamsize _N, char *_P = 0,
  10317. _Strstate _S = _Strzero)
  10318. {_Init(_N, _G, _P, _S); }
  10319. strstreambuf(unsigned char *_G, streamsize _N,
  10320. unsigned char *_P = 0)
  10321. {_Init(_N, (char *)_G, (char *)_P); }
  10322. strstreambuf(const char *_G, streamsize _N)
  10323. {_Init(_N, (char*)_G, 0, _Constant); }
  10324. strstreambuf(const unsigned char *_G, streamsize _N)
  10325. {_Init(_N, (char *)_G, 0, _Constant); }
  10326. virtual ~strstreambuf();
  10327. void freeze(bool = 1);
  10328. char *str()
  10329. {freeze(); return (gptr()); }
  10330. streamsize pcount() const
  10331. {return (pptr() == 0 ? 0 : pptr() - pbase()); }
  10332. #if _HAS_SIGNED_CHAR
  10333. strstreambuf(signed char *_G, streamsize _N,
  10334. signed char *_P = 0)
  10335.  
  10336. {_Init(_N, (char *)_G, (char *)_P); }
  10337. strstreambuf(const signed char *_G, streamsize _N)
  10338. {_Init(_N, (char *) _G. 0, _Constant); }
  10339. #endif /* _HAS_SIGNED_CHAR */
  10340. protected:
  10341. virtual int overflow(int = EOF);
  10342. virtual int pbackfail(int = EOF);
  10343. virtual int underflow();
  10344. virtual streampos seekoff(streamoff, ios::seekdir,
  10345. ios::openmode = ios::in ios::out);
  10346. virtual streampos seekpos(streampos,
  10347. ios::openmode = ios::in ios::out);
  10348. void_Init(int = 0, char * = 0, char * = 0,
  10349. _Strstate = _Strzero);
  10350. void_Tidy();
  10351. _Strstate _Strmode;
  10352. private:
  10353. char *_Pendsave, *_Seekhigh;
  10354. int _Alsize;
  10355. void *(*_Palloc)(size_t);
  10356. void (*_Pfree)(void *);
  10357. };
  10358. _BITMASK_OPS(strstreambuf::_Strstate)
  10359. // class istrstream
  10360. class istrstream : public istream {
  10361. public:
  10362. istrstream(const char *_S)
  10363. : istream(&_Sb), _Sb(S, 0) {}
  10364. istrstream(const char *_S, streamsize _N)
  10365. : istream(&_Sb), Sb(_S, _N) {}
  10366. istrstream{char * _S)
  10367. : istream(&_Sb), _Sb((const char *)_S, 0) {}
  10368. istrstream(char *_S, int _N
  10369. :istream(&_Sb), _Sb((const char *)_S, N) {}
  10370. virtual ~istrstream();
  10371. strstreambuf *rdbuf() const
  10372. {return ((strstreambuf *)&_Sb); }
  10373. char *str()
  10374. {return ( _Sb.str()); }
  10375. private:
  10376. strstreambuf _Sb;
  10377. };
  10378. //class ostrstream
  10379. class ostrstream : public ostream {
  10380. public:
  10381. ostrstream( )
  10382. : ostream(&_Sb), Sb() {}
  10383. ostrstream(char *, streamsize, openmode = out);
  10384. virtual ~ostrstream();
  10385. strstreambuf *rdbuf() const
  10386. {return ((strstreambuf *)&_Sb); }
  10387. void freeze(int _F = 1)
  10388. {_Sb.freeze(_F); }
  10389. char *str()
  10390. {return (_Sb.str()); }
  10391. int pcount() const
  10392. {return (_Sb.pcount()); }
  10393. private:
  10394. strstreambuf _Sb;
  10395.  
  10396. };
  10397. #endif /* _STRSTREAM_ */
  10398.  
  10399.  
  10400. Listing 2 The file strstrea.c
  10401. // strstreambuf -- strstreambuf basic members
  10402. #include <limits.h>
  10403. #include <string.h>
  10404. #include <strstream>
  10405.  
  10406. strstreambuf::~strstreambuf()
  10407. { // destruct a strstreambuf
  10408. _Tidy();
  10409. }
  10410.  
  10411. void strstreambuf::_Init(int n, char *gp, char *pp,
  10412. _Strstate mode)
  10413. { // initialize with possibly static buffer
  10414. streambuf::_Init();
  10415. _Pendsave = 0;
  10416. _Seekhigh = 0;
  10417. _Palloc = 0;
  10418. _Pfree = 0;
  10419. _Strmode = mode;
  10420. if (gp == 0)
  10421. { // make dynamic
  10422. _Alsize = _MINSIZE <= n ? n : _ALSIZE;
  10423. _Strmode = _Dynamic;
  10424. }
  10425. else if (_Strmode & _Dynamic)
  10426. { // initialize a stringbuf from string
  10427. _Alsize = ALSIZE;
  10428. if (0 < n)
  10429. { // copy string
  10430. char *s = new char[n];
  10431. if (S == 0)
  10432. _Nomemory();
  10433. memcpy(s, gp, n);
  10434. _Seekhigh = s + n;
  10435. if (!(_Strmode & _Noread))
  10436. setg(s, s, s + n);
  10437. if (!(_Strmode & _Constant))
  10438. { // make output string and
  10439.  
  10440. setp(s, s + n);
  10441. if (!gptr())
  10442. setg(s, s, s);
  10443. }
  10444. _Strmode = _Allocated;
  10445. }
  10446. }
  10447. else
  10448. { // make static
  10449. int size = n < 0 ? INT_MAX : n == 0 ? strlen(gp) : n;
  10450. _Alsize = 0;
  10451. _Seekhigh = gp + size;
  10452. if (pp == 0)
  10453. setg(gp, gp, gp + size);
  10454. else
  10455.  
  10456. { // make writable too
  10457. if (pp < gp)
  10458. pp = gp;
  10459. else if (gp + size < pp)
  10460. pp = gp + size;
  10461. setp(pp, gp + size);
  10462. setg(gp, gp, pp);
  10463. }
  10464. }
  10465. }
  10466.  
  10467. void strstreambuf::_Tidy()
  10468. { // discard any allocated storage
  10469. if ((_Strmode & (_Allocated) _Frozen)) != _Allocated)
  10470. ;
  10471. else if (_Pfree != 0)
  10472. (*_Pfree)(eback());
  10473. else
  10474. delete [] eback();
  10475. _Seekhigh = 0;
  10476. _Strmode &= ~(_Allocated _Frozen);
  10477. }
  10478.  
  10479.  
  10480. Listing 3 The file strstpro.c
  10481. // strstpro -- strstreambuf protected members
  10482. #include <string.h>
  10483. #include <strstream>
  10484.  
  10485. int strstreambuf::overflow(int ch)
  10486. { // try to extend write area
  10487. if (pptr() != ) && pptr() < epptr())
  10488. return (* _Pn()++ = ch);
  10489. else if (!(_Strmode & _Dynamic)
  10490.  _Strmode & (_Constant) _Frozen))
  10491. return (EOF);
  10492. else
  10493. { // okay to extend
  10494. int osize = gptr() == 0 ? 0 : epptr() - eback();
  10495. int nsize = osize + _Alsize;
  10496. char *p = _Palloc != 0 ? (char *)(*_Palloc)(nsize)
  10497. : new char[nsize];
  10498. if (p == 0)
  10499. return (EOF);
  10500. if (0 < osize)
  10501. memcpy(p, eback(), osize);
  10502. else if (_ALSIZE < _Alsize)
  10503. _Alsize = _ALSIZE;
  10504. if (!(_Strmode & -Allocated))
  10505. ;
  10506. else if (_Pfree != 0)
  10507. (*_Pfree)(eback());
  10508. else
  10509. delete [] eback();
  10510. _Strmode =_Allocated;
  10511. if (osize == 0)
  10512. { // setup new buffer
  10513. _Seekhigh = p;
  10514. setp(p, p + nsize);
  10515.  
  10516. setg(p, p, p);
  10517. }
  10518. else
  10519. { // revise old pointers
  10520. _Seekhigh = _Seekhigh - eback() + p;
  10521. setp(pbase() - eback() + p, pptr() - eback() + p,
  10522. p + nsize);
  10523. if (_Strmode &_Noread)
  10524. setg(p, p, p);
  10525. else
  10526. setg(p, gptr() - eback() + p, pptr() + 1);
  10527. }
  10528. return (ch == EOF ? 0 : (*_Pn()++ = ch));
  10529. }
  10530. }
  10531. int strstreambuf::pbackfail(int ch)
  10532. { // try to putback a character
  10533. if (gptr() == 0 gptr() <= eback()
  10534.  ch != EOF && (unsigned char)ch != _Gn()[-1]
  10535. &&_Strmode &_Constant)
  10536. return (EOF);
  10537. else
  10538. { // safe to back up
  10539. gbump(-1);
  10540. return (ch == EOF ? 0: (*_Gn() = ch));
  10541. }
  10542. }
  10543.  
  10544. int strstreambuf::underflow()
  10545. { // read only if read position available
  10546. if (gptr() == 0)
  10547. return (EOF);
  10548. else if (gptr() < egptr())
  10549. return (*_Gn());
  10550. else if (_Strmode & _Noread pptr() == 0
  10551.  pptr() <= gptr() &&_Seekhigh <= gptr())
  10552. return (EOF);
  10553. else
  10554. { // update_Seekhigh and expand read region
  10555. if (_Seekhigh < pptr())
  10556. _Seekhigh = pptr();
  10557. setg(eback(), gptr(),_Seekhigh);
  10558. return (*_Gn());
  10559. }
  10560. }
  10561.  
  10562.  
  10563. Listing 4 The file strstfre.c
  10564. // strstfreeze -- strstreambuf::freeze(bool)
  10565. #include <strstream>
  10566.  
  10567. void strstreambuf::freeze(bool freezeit)
  10568. { // freeze a dynamic string
  10569. if (freezeit && !(_Strmode & _Frozen))
  10570. { // disable writing
  10571. _Strmode = _Frozen;
  10572. _Pendsave = epptr();
  10573. setp(pbase(), pptr(), eback());
  10574. }
  10575.  
  10576. else if (!freezeit && _Strmode & _Frozen)
  10577. { // re-enable writing
  10578. _Strmode &= ~_Frozen;
  10579. setp(pbase(), pptr(), _Pendsave);
  10580. }
  10581. }
  10582.  
  10583.  
  10584. Listing 5 The file strstpos.c
  10585. // strstpos -- strstreambuf positioning members
  10586. #include <strstream>
  10587.  
  10588. streampos strstreambuf::seekoff(streamoff off,
  10589. ios::seekdir way, ios::openmode which)
  10590. { // seek by specified offset
  10591. if (pptr() != 0 && _Seekhigh < pptr())
  10592. _Seekhigh = pptr();
  10593. if (which & ios::in && gptr() != 0)
  10594. { // set input (and maybe output) pointer
  10595. if (way == ios::end)
  10596. off += _Seekhigh - eback();
  10597. else if (way == ios::cur && !(which & ios::out))
  10598. off += gptr() - eback();
  10599. else if (way != ios::beg off == BADOFF)
  10600. off= _BADOFF;
  10601. if (0 <= off && off <= _Seekhigh - eback())
  10602. { // set one or two pointers
  10603. gbump(eback() - gptr() + off);
  10604. if (which & ios::out && pptr() != 0)
  10605. setp(pbase(), gptr(), epptr());
  10606. }
  10607. else
  10608. off = _BADOFF;
  10609. }
  10610. else if (which & ios::out && pptr() != 0)
  10611. { // set only output pointer
  10612. if (way == ios::end)
  10613. off += _Seekhigh - eback();
  10614. else if (way == ios::cur)
  10615. off += pptr() - eback();
  10616. else if (way != ios::beg off == _BADOFF)
  10617. off= _BADOFF;
  10618. if (0 <= off && off <= _Seekhigh - eback())
  10619. pbump(eback() - pptr() + off);
  10620. else
  10621. off = _BADOFF;
  10622. }
  10623. else // nothing to set
  10624. off = _BADOFF;
  10625. return (streampos(off));
  10626. }
  10627. streampos strstreambuf::seekpos(streampos sp,
  10628. ios::openmode which)
  10629. { // seek to memorized position
  10630. streamoff off = sp.offset();
  10631. if (pptr() != 0 &&_Seekhigh < pptr())
  10632. _Seekhigh = pptr();
  10633. if (off == _BADOFF)
  10634. ;
  10635.  
  10636. else if (which & ios::in && gptr() != 0)
  10637. { // set input (and maybe output) pointer
  10638. if (0 <= off && off <= _Seekhigh - eback())
  10639. { // set valid offset
  10640. gbump(eback() - gptr() + off);
  10641. if (which & ios::out && pptr() != 0)
  10642. setp(pbase(), gptr(), epptr());
  10643. }
  10644. else
  10645. off = _BADOFF;
  10646. }
  10647. else if (which & ios::out && pptr() != 0)
  10648. { // set output pointer
  10649. if (0 <= off && off <= _Seekhigh - eback())
  10650. pbump(eback() - pptr() + off);
  10651. else
  10652. off = _BADOFF;
  10653. }
  10654. else // nothing to set
  10655. off = _BADOFF;
  10656. return (streampos(off));
  10657. }
  10658.  
  10659.  
  10660. Listing 6 The file istrstre.c
  10661. // istrstream -- istrstream basic members
  10662. #include <strstream>
  10663.  
  10664. istrstream::~istrstream()
  10665. { // destruct an istrstream
  10666. }
  10667.  
  10668.  
  10669. Listing 7 The file ostrstre.c
  10670. // ostrstream -- ostrstream basic members
  10671. #include <string.h>
  10672. #include <strstream>
  10673. _STD_BEGIN
  10674.  
  10675. ostrstream::ostrstream(char *s, int n, openmode mode)
  10676. : ios(&_Sb), ostream(&_Sb),
  10677. _Sb(s, n, s == 0 !(mode & app) ? s : s + strlen(s))
  10678. { // write at terminating null (if there)
  10679. }
  10680.  
  10681. ostrstream::~ostrstream()
  10682. { // destruct an ostrstream
  10683. }
  10684.  
  10685.  
  10686.  
  10687.  
  10688.  
  10689.  
  10690.  
  10691.  
  10692.  
  10693.  
  10694.  
  10695.  
  10696.  
  10697.  
  10698.  
  10699.  
  10700.  
  10701.  
  10702.  
  10703.  
  10704.  
  10705.  
  10706.  
  10707.  
  10708.  
  10709.  
  10710.  
  10711.  
  10712.  
  10713.  
  10714.  
  10715.  
  10716.  
  10717.  
  10718.  
  10719.  
  10720.  
  10721.  
  10722.  
  10723.  
  10724.  
  10725.  
  10726.  
  10727.  
  10728.  
  10729.  
  10730.  
  10731.  
  10732.  
  10733.  
  10734.  
  10735.  
  10736.  
  10737.  
  10738.  
  10739.  
  10740.  
  10741.  
  10742.  
  10743.  
  10744.  
  10745.  
  10746.  
  10747.  
  10748.  
  10749.  
  10750.  
  10751.  
  10752.  
  10753.  
  10754.  
  10755.  
  10756.  
  10757.  
  10758.  
  10759. Questions and Answers
  10760.  
  10761.  
  10762. qsort and Static Functions
  10763.  
  10764.  
  10765.  
  10766.  
  10767. Kenneth Pugh
  10768.  
  10769.  
  10770. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++
  10771. language courses for corporations. He is the author of All On C, C for COBOL
  10772. Programmers, and UNIX for MS-DOS Users, and was a member of the ANSI C
  10773. committee. He also does custom C/C++ programming and provides
  10774. SystemArchitectonicssm services. He is president of the Independent Computer
  10775. Consultants Association. His address is 4201 University Dr., Suite 102,
  10776. Durham, NC 27707, You may fax questions for Ken to (919) 489-5239. Ken also
  10777. receives email at kpugh@allen.com (Internet) and on Compuserve 70125,1142.
  10778.  
  10779.  
  10780. Q
  10781. In your column in the May 1994 issue of CUJ (which, by the way, arrived here a
  10782. week or so after the July issue -- apparently the boat carrying May's issue to
  10783. the Antipodes took a lengthy detour), you discuss a problem in using the qsort
  10784. function to sort data within a C++ class. You describe a Catch-22 situation,
  10785. in which the comparison function passed to qsort must be a member function if
  10786. it is to access private data members of the class, but qsort is unable to call
  10787. a C++ function because its calling convention differs from that of a C
  10788. function.
  10789. There is a way to circumvent this problem, which works with all the C++
  10790. compilers I have tested (Borland C/C++ 3.1, Microsoft C/C++ 7.0, and GNU g++
  10791. 1.39.1). The trick is to declare the comparison function to be a static member
  10792. function (See Listing 1). Static member functions don't receive a this
  10793. argument and (at least in these compilers) use the same calling conventions as
  10794. ordinary C functions. Unfortunately, however, I suspect there is no guarantee
  10795. that this technique will work with all C++ compilers. Does the proposed C++
  10796. standard have anything to say about this? Perhaps the standards committee
  10797. might consider requiring static member functions to use the same calling
  10798. conventions as C functions, as it would facilitate use of the standard qsort
  10799. and bsearch C library functions.
  10800. By the way, the program listing which accompanied this discussion in the May
  10801. issue contained at least two errors, and also raised a couple of questions in
  10802. my mind. First the (minor) errors: 1) The declaration of class A lacks a
  10803. terminating semicolon. 2) The use of compare_function in the sort must be
  10804. preceded by a prototype. I have some more questions on Listing 1 (reproduced
  10805. here -- see Listing 1)
  10806. 1) Should the declarations of first and second in compare_function be of const
  10807. DataType * or const A::DataType *? My guess would have been the latter, as
  10808. DataType is a private member of class A, and really shouldn't be "visible" to
  10809. an external function.
  10810. 2) When the latter form is used, the Borland compiler compiles it without
  10811. error; the Microsoft compiler fails with the error message: "'DataType' :
  10812. cannot access 'private' member declared in class 'A'." Which is the correct
  10813. behavior?
  10814. Listing 2 is a program which demonstrates my approach of declaring the
  10815. comparison function to be a static member function.
  10816. I do enjoy reading your columns. Keep up the good work!
  10817. Cheers,
  10818. Eric Zurcher
  10819. Canberra, Australia
  10820. A
  10821. For the purpose of implementation hiding, a static function would be better
  10822. than a friend function, if it were guaranteed to work on all compilers. For
  10823. the compilers you listed, static C++ functions do appear to have the same
  10824. calling sequence as C functions. Other readers have responded with similar
  10825. statements about these compilers. I do not believe this feature is in the
  10826. standard.
  10827. Thank you for the sharp-eyed corrections to my program. My grammar checker
  10828. does not look for a terminating ';'. In response to your first question, your
  10829. comparison function should use:
  10830. const A::DataType *first = (A::DataType*) a;
  10831. const A::DataType *second = (A::DataType*) b;
  10832. This is explained in detail on pages 186-187 of The Annotated C++ Reference
  10833. Manual (ARM) by Ellis and Stroustrop [1]. For those who do not have that
  10834. reference handy, I'll paraphrase the ARM's explanation. The rules on nested
  10835. classes have flip-flopped in recent years.
  10836. Class names (as well as structure names) now follow the same rules of scoping
  10837. as other names. In C, structures did not have scoping rules and therefore you
  10838. could use these declarations:
  10839. const DataType *first = (DataType*) a;
  10840. const DataType *second = (DataType*) b;
  10841. The declaration of a structure template inside another structure template was
  10842. just a convenience and had no scope meaning.
  10843. In C++, using a nested class in a non-class function puts it in the scope of
  10844. that class. A program requires scoping information if a reference appears
  10845. outside of that class (i.e., its template and its member functions). This
  10846. scope applies to all nested types. For example, you commonly see:
  10847. class A
  10848. {
  10849. public:
  10850. enum Enumeration {One, Two,
  10851. Three};
  10852. //...
  10853. };
  10854. and a non-member function as:
  10855. void non_member_function()
  10856. {
  10857. A:Enumeration a;
  10858. //. . .
  10859. };
  10860. Access is a separate issue from scope. In answer to your second question,
  10861. access control applies to both members (function and data) and nested types
  10862. (ARM, p. 239). Therefore the attempt to access Datatype by the comparison
  10863. function should not compile.
  10864. Your second program is a bit cleaner on the access side. It eliminates the
  10865. access problems of the first example, at the expense of a slightly more
  10866. complex looking class interface. I will be glad when the new namespace
  10867. mechanism becomes commonplace. Then we will have the same access protection as
  10868. nested classes, but without the visual clutter.
  10869. Reference
  10870. [1] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference
  10871. Manual (Addison-Wesley, 1994).
  10872.  
  10873. Listing 1 An attempt to use qsort in C++
  10874. #include <stdlib. h>
  10875.  
  10876.  
  10877. int compare_function(const void * a, const void * b);
  10878.  
  10879. class A
  10880. {
  10881. private:
  10882. struct DataType
  10883. {
  10884. double x,y;
  10885. } * data;
  10886. int size;
  10887. public:
  10888. A();
  10889. A(int n, double *x, double *y);
  10890. void sort();
  10891. };
  10892.  
  10893. void A::sort()
  10894. {
  10895. qsort (data, size, sizeof(DataType), compare_function);
  10896. }
  10897.  
  10898. int compare_function(const void * a, const void * b)
  10899. {
  10900. const DataType *first = (DataType*) a;
  10901. const DataType *second = (DataType*) b;
  10902. // Ought these declaration be as follows ?
  10903. // const A::DataType *first = (A::DataType*) a;
  10904. // const A::DataType *second = (A::DataType*) b;
  10905. if (first->x > second->x)
  10906. return 1;
  10907. else if (first->x < second->x)
  10908. return -1;
  10909. else
  10910. return 0;
  10911. }
  10912. /* End of File */
  10913.  
  10914.  
  10915. Listing 2 using qsort in C++ made possible via static member function
  10916. #include <stdlib.h>
  10917. #include <iostream.h>
  10918.  
  10919. class A
  10920. {
  10921. private:
  10922. struct DataType
  10923. {
  10924. double x,y;
  10925. static int compare_function(const void * a,
  10926. const void * b)
  10927. {
  10928. const DataType *first = (DataType*) a;
  10929. const DataType *second: (DataType*) b;
  10930. if (first->x > second->x)
  10931. return 1;
  10932. else if (first->x < second->x)
  10933. return -1;
  10934. else
  10935.  
  10936.  
  10937. return 0;
  10938. }
  10939. } * data;
  10940. int size;
  10941. public:
  10942. A(int n, double * x, double * y);
  10943. ~A();
  10944. void sort();
  10945. void list();
  10946. };
  10947.  
  10948. A::A(int n, double * x, double * y)
  10949. {
  10950. size: n;
  10951. data = new DataType[size];
  10952. for (int i = 0; i < size; i++)
  10953. {
  10954. data[i].x = x[i];
  10955. data[i].y = y[i];
  10956. }
  10957. };
  10958.  
  10959.  
  10960. A::~A()
  10961. {
  10962. delete [] data;
  10963. }
  10964.  
  10965. void A::sort()
  10966. {
  10967. qsort (data, size, sizeof(DataType),
  10968. DataType::compare_function);
  10969. }
  10970.  
  10971. void A::list()
  10972. {
  10973. for (int i = 0; i < size; i++)
  10974. {
  10975. cout << data[i].x << " " << data[i].y << endl;
  10976. }
  10977. }
  10978.  
  10979. main()
  10980. {
  10981. double xarray[5] = {4.0, 2.0, 3.0, 5.0, 1.0};
  10982. double yarray[5] = {0.1, 0.2, 0.3, 0.4, 0.5}:
  10983. A AInst (3, xarray, yarray);
  10984. cout << "Original list" << endl;
  10985. AInst.list();
  10986. AInst.sort();
  10987. cout << endl << "Sorted list" << endl;
  10988. AInst.list();
  10989. return 0;
  10990. }
  10991. /* End of File */
  10992.  
  10993.  
  10994.  
  10995.  
  10996.  
  10997.  
  10998.  
  10999.  
  11000.  
  11001.  
  11002.  
  11003.  
  11004.  
  11005.  
  11006.  
  11007.  
  11008.  
  11009.  
  11010.  
  11011.  
  11012.  
  11013.  
  11014.  
  11015.  
  11016.  
  11017.  
  11018.  
  11019.  
  11020.  
  11021.  
  11022.  
  11023.  
  11024.  
  11025.  
  11026.  
  11027.  
  11028.  
  11029.  
  11030.  
  11031.  
  11032.  
  11033.  
  11034.  
  11035.  
  11036.  
  11037.  
  11038.  
  11039.  
  11040.  
  11041.  
  11042.  
  11043.  
  11044.  
  11045.  
  11046.  
  11047.  
  11048.  
  11049.  
  11050.  
  11051.  
  11052.  
  11053.  
  11054.  
  11055.  
  11056.  
  11057.  
  11058.  
  11059.  
  11060. Code Capsules
  11061.  
  11062.  
  11063. The Standard C Library, Part 2
  11064.  
  11065.  
  11066.  
  11067.  
  11068. Chuck Allison
  11069.  
  11070.  
  11071. Chuck Allison is a regular columnist with CUJ and a Senior Software Engineer
  11072. in the Information and Communication Systems Department of the Church of Jesus
  11073. Christ of Latter Day Saints in Salt Lake City. He has a B.S. and M.S. in
  11074. mathematics, has been programming since 1975, and has been teaching and
  11075. developing in C since 1984. His current interest is object-oriented technology
  11076. and education. He is a member of X3J16, the ANSI C++ Standards Committee.
  11077. Chuck can be reached on the Internet at 72640.1507@compuserve.com.
  11078.  
  11079.  
  11080. Last month I divided the fifteen headers of the Standard C Library into three
  11081. Groups, each representing different levels of mastery (see Table 1 through
  11082. Table 3). I continue this month by exploring Group II.
  11083.  
  11084.  
  11085. Group II: For the "Polished" C Programmer
  11086.  
  11087.  
  11088.  
  11089.  
  11090. <assert.h>
  11091.  
  11092.  
  11093. Well-organized programs provide key points where you can make assertions, such
  11094. as "the index points to the next open array element." It is important to test
  11095. these assertions during development and to document them for the maintenance
  11096. programmer (which, of course, is often yourself). ANSI C provides the assert
  11097. macro for this purpose. You could represent the assertion above, for example,
  11098. as
  11099. #include <assert.h>
  11100. . . .
  11101. assert(nitems < MAXITEMS && i
  11102. == nitems);
  11103. . . .
  11104. If the condition holds, all is well and execution continues. Otherwise, assert
  11105. prints a message containing the condition, the file name, and line number, and
  11106. then calls abort to terminate the program.
  11107. Use assert to validate the internal logic of your program. If a certain thread
  11108. of execution is supposed to be impossible, say so with the call assert(0), as
  11109. in: 
  11110. switch (color)
  11111. {
  11112. case RED:
  11113. . . .
  11114. case BLUE:
  11115. . . .
  11116. case GREEN:
  11117. . . .
  11118. default:
  11119. assert(0);
  11120. }
  11121. assert is also handy for validating parameters. A function that takes a string
  11122. argument, for example, could do the following:
  11123. char * f(char *s)
  11124. {
  11125. assert(s);
  11126. . . .
  11127. }
  11128. Assertions are for logic errors, of course, not for run-time errors. A logic
  11129. error is one you could have avoided by correct design. For example, no action
  11130. on the user's part should be able to create a null pointer -- that's clearly
  11131. your fault, so it is appropriate to use assert in such cases. On the other
  11132. hand, a run-time error, such as a memory failure, requires more bulletproof
  11133. exception handling.
  11134. When your code is ready for production, you should have caught all the bugs,
  11135. so you should turn off assertion processing. To do so, you can either include
  11136. the statement
  11137. #define NDEBUG
  11138. in the beginning of the code, or define the macro on the command line if your
  11139. compiler allows it (most use the -D switch). With NDEBUG defined, all
  11140. assertions expand to a null macro, but the text remains in the code for
  11141. documentation.
  11142.  
  11143.  
  11144. <limits.h>
  11145.  
  11146.  
  11147.  
  11148. By definition, portable programs do not depend in any way on the particulars
  11149. of any one environment. Even assuming that all bytes consist of eight bits is
  11150. not safe. The header <limits.h> defines the upper and lower bounds for all
  11151. integer types (see Table 4). The program in Listing 1 toggles each bit in an
  11152. integer on and off. It uses the value CHAR_BIT, defined in <limits.h>, as the
  11153. number of bits in a byte, to determine the number of bits in an integer. As
  11154. Listing 2 illustrates, you can also use <limits.h> to determine the most
  11155. efficient data type to use for signed numeric values that must span a certain
  11156. range.
  11157.  
  11158.  
  11159. <stddef.h>
  11160.  
  11161.  
  11162. The header <stddef.h> defines three type synonyms and two macros (see Table
  11163. 5). When you subtract two pointers which refer to elements of the same array
  11164. (or one position past the end of the array), you get back the difference of
  11165. the two corresponding subscripts, which will be the number of elements between
  11166. the pointers. The type of the result is either an int or a long, whichever is
  11167. appropriate for your memory model. <stddef.h> defines the appropriate type as
  11168. ptrdiff_t.
  11169. The sizeof operator returns a value of type size_t. size_t is the unsigned
  11170. integer type that can represent the size of the largest data object you can
  11171. declare in your environment. Usually an unsigned int or unsigned long is
  11172. sufficient to represent this size. size_t is usually the unsigned counterpart
  11173. of the type used for ptrdiff_t. If you look through the headers in the
  11174. Standard C Library, you'll find extensive use of type size_t. It is good idea
  11175. to use size_t for all array indices and for pointer arithmetic (i.e., adding
  11176. an offset to a pointer), unless for some reason you need the ability to count
  11177. down past zero, which unsigned integers can't do.
  11178. The type wchar_t holds a wide character, an implementation- defined integral
  11179. type for representing characters beyond standard ASCII. You define wide
  11180. character constants with a preceding L, as in:
  11181. #include <stddef.h>
  11182. wchar_t c = L'a';
  11183. wchar_t *s = L"abcde";
  11184. As Listing 3 illustrates, my environment defines a wide character as a
  11185. two-byte integer. This coincides nicely with the emerging 16-bit Unicode
  11186. standard for international characters (see the sidebar "Character Sets"). The
  11187. <stdlib.h> functions listed in Table 6 use type wchar_t. Amendment 1, an
  11188. official addendum to Standard C accepted in 1994, defines many additional
  11189. functions for handling wide and multi-byte characters. For more detailed
  11190. information, see P. J. Plauger's columns in the April 1993 and May 1993 issues
  11191. of CUJ.
  11192. The NULL macro is the universal zero-pointer constant, defined as one of 0,
  11193. 0L, or (void *) 0. It is almost always a bad idea to assume any one of these
  11194. definitions in a program -- for safety, just include a header that defines
  11195. NULL (stddef.h, stdio.h, stdlib.h, string.h, locale.h) and let the system
  11196. figure out the correct representation. <stddef.h> is handy when you need only
  11197. NULL defined in a translation unit and nothing else.
  11198. The offsetof macro returns the offset in bytes from the beginning of a
  11199. structure to one of its members. Due to address alignment contraints, some
  11200. implementations insert unused bytes after members in a structure, so you can't
  11201. assume that the offset of a member is just the sum of the sizes of the members
  11202. that precede it. For example, the program in Listing 4 exposes a one-byte gap
  11203. in the Person structure after the name member, allowing the age member to
  11204. start on a word boundary (a word is two bytes here). Use offsetof if you need
  11205. an explicit pointer to a structure member:
  11206. struct Person p;
  11207. int *age_p;
  11208. age_p = (int*) ((char*)&p
  11209. + offsetof(struct Person, age));
  11210.  
  11211.  
  11212. <time.h>
  11213.  
  11214.  
  11215. Most environments provide some mechanism for keeping time. time.h provides the
  11216. type clock_t, a numeric type that tracks processor time (see Table 7). The
  11217. clock function returns an implementation-defined value of type clock_t that
  11218. represents the current processor time. Unfortunately, what is meant by
  11219. "processor time" varies across platforms, so clock by itself isn't very
  11220. useful. You can, however, compare processor times, and then divide by the
  11221. constant CLOCKS_PER_SEC, thus rendering the number of seconds elapsed between
  11222. two points in time. The program in Listing 5 thru Listing 7 uses clock to
  11223. implement such stopwatch functions.
  11224. The rest of the functions in <time.h> deal with calendar time. The time
  11225. function returns a system-dependent encoding of the current date and time as
  11226. type time_t (usually a long). The function localtime decodes a time_t into a
  11227. struct tm (see Listing 8). The asctime function returns a text representation
  11228. of a decoded time in a standard format, namely
  11229. Mon Nov 28 14:59:03 1994
  11230. For more detail, see the Code Capsule "Time and Date Processing in C," CUJ,
  11231. January 1993.
  11232. I'll conclude this series on the Standard C Library next month with a
  11233. discussion of the functionality of the headers in Group III. 
  11234. Character Sets
  11235. A script is a set of symbols used to convey textual information. There are
  11236. over 30 major scripts in the world. Some scripts, such as Roman and Cyrillic,
  11237. serve many languages. World scripts can be categorized according to the
  11238. hierarchy in Figure 1.
  11239. Most scripts are alphabetic. The Han script used by Chinese, Japanese, and
  11240. Korean, however, is an ideographic (or more accurately, logographic) script.
  11241. Each Han character represents an object or concept -- these languages have no
  11242. notion of words composed of letters from an alphabet.
  11243. A character set is a collection of text symbols with an associated numerical
  11244. encoding. The ASCII character set with which most of us are familiar maps the
  11245. letters and numerals used in our culture to integers in the range [32, 126],
  11246. with special control codes filling out the 7-bit range [0, 127]. As the 'A' in
  11247. the acronym suggests, this is strictly an American standard. Moreover, this
  11248. standard only specifies half of the 256 code points available in a single
  11249. 8-bit byte.
  11250. There are a number of extended ASCII character sets that fill the upper range
  11251. [128, 255] with graphics characters, accented letters, or non-Roman
  11252. characters. Since 256 code points are not enough to cover even the Roman
  11253. alphabets in use today, there are five separate, single-byte standards for
  11254. applications that use Roman characters (see Figure 2).
  11255. The obvious disadvantage of single-byte character sets is the difficulty of
  11256. processing data from distinct regions, such as Greek and Hebrew, in a single
  11257. application. Single-byte encoding is wholly unfit for Chinese, Japanese, and
  11258. Korean, since there are thousands of Han characters.
  11259. One way to increase the number of characters in a single encoding is to map
  11260. characters to more than one byte. A multibyte character set maps a character
  11261. to a variable-length sequence of one or more byte values. In one popular
  11262. encoding scheme, if the most significant bit of a byte is zero, the character
  11263. it represents is standard ASCII; if not, that byte and the next form a 16-bit
  11264. code for a local character.
  11265. Multibyte encodings are storage efficient since they have no unused bytes, but
  11266. they require special algorithms to compute indices into a string, or to find
  11267. string length, since characters are represented as a variable number of bytes.
  11268. To overcome string indexing problems, Standard C defines functions that
  11269. process multibyte characters, and that convert multibyte strings into
  11270. wide-character strings (i.e., strings of wchar_t, usually two-byte
  11271. characters). Unfortunately, these multi-byte and wide-character functions are
  11272. commonly available only on XPG4-compliant UNIX platforms and Japanese
  11273. platforms. The recently approved Amendment 1 to the C Standard defines many
  11274. additional functions for processing seqences of mutli byte and wide
  11275. characters, and should entice U.S. vendors to step out of their cultural
  11276. comfort zone.
  11277.  
  11278.  
  11279. Code Pages
  11280.  
  11281.  
  11282. Since standard ASCII consists of only 128 code points, there are 128 more
  11283. waiting to be used in an eight-bit character encoding. It has been common
  11284. practice to populate the upper 128 codes with characters suitable for local
  11285. use. The combination of values 128-255 together with ASCII is called a code
  11286. page under MS-DOS. The default code page for the IBM PC in the United States
  11287. and much of Europe (#437) includes some box-drawing and other graphics
  11288. characters, and Roman characters with diacritical marks. Other MS-DOS code
  11289. pages include:
  11290. 863 Canadian-French
  11291. 850 Multi-Lingual (Latin-1)
  11292. 865 Nordic
  11293. 860 Portuguese
  11294. 852 Slavic (Latin-2)
  11295. Non-U.S. versions of MS-DOS define other code pages. You can switch between
  11296. code pages in MS-DOS applications, but not in U.S. Microsoft Windows (except
  11297. in a DOS window). Only one code page remains active for Windows-hosted
  11298. applications throughout an entire Windows session. Different versions of
  11299. Windows have code pages appropriate for their region. For example, Windows-J
  11300. for Japan uses a code page based on Shift-JIS. Windows 95 (a.k.a. Chicago)
  11301. will support full code-page switching.
  11302. Since code pages use code points in the range [128, 255], it is important to
  11303. avoid depending on or modifying the high-bit value in any byte of your
  11304. program's data. A program that follows this discipline is called 8-bit clean.
  11305.  
  11306.  
  11307. Character Set Standards
  11308.  
  11309.  
  11310. Seven-bit ASCII is the world's most widely-used character set. ISO 646 is
  11311. essentially ASCII with a few codes subject to localization. For example, the
  11312. currency symbol, code point 0x24, is '$' only in the United States, and is
  11313. allowed to "float" to adhere to local conventions. ISO 646 is sometimes called
  11314. the portable character set (PCS) and is the standard alphabet for programming
  11315. languages.
  11316. ISO 8859 is a standard that takes advantage of all 256 single-byte code points
  11317. to define nine eight-bit mappings, to nine selected alphabets (see Figure 2).
  11318. Each of these mappings retains ISO 646 as a subset, hence they differ mainly
  11319. in the upper 128 code points. Some of these mappings are the basis for MS-DOS
  11320. code pages.
  11321.  
  11322. There is no official ISO standard for multibyte character sets in the Far
  11323. East. However, each region of the Far East has its own local (national)
  11324. standards. PC-industry standards, based on national standards, are also in
  11325. common use in the Far East. Examples include Eten, Big Five, and Shift JIS.
  11326.  
  11327.  
  11328. ISO 1O646
  11329.  
  11330.  
  11331. To simplify the development of internationalized applications, ISO developed
  11332. the Universal Multiple-Octet Coded Character Set (ISO 10646), to accommodate
  11333. all characters from all significant modern languages in a single encoding. An
  11334. octet is a contiguous, ordered collection of eight bits, which is a byte on
  11335. most systems. ISO 10646 allows for 2,147,483,648 (231) characters, although
  11336. only 34,168 have been defined. It is organized into 128 groups, each group
  11337. containing 256 planes of 65,536 characters each (256 rows x 256 columns.
  11338. Any one of the 231 characters can be addressed by four octets, representing
  11339. respectively the group, plane, row, and column of its location in the
  11340. four-dimensional space. Consequently, ISO 10646 is a 32-bit character
  11341. encoding. ASCII code points are a subset of ISO 10646 -- you just add leading
  11342. zeroes to fill out 32 bits. For example, the encoding for the letter 'a' is
  11343. 00000061 hexadecimal (i.e., Group 0, Plane 0, Row 0, Column 0x61).
  11344. Plane 0 of Group 0 is the only one of the 32,768 planes that has been
  11345. populated to date. It is called the Basic Multi-Lingual Plane (BMP). ISO 10646
  11346. allows conforming implementations to be BMP-based, i.e., requiring only two
  11347. octets, representing the row and column within the BMP. The full four-octet
  11348. form of encoding is called UCS-4, and the two-octet form UCS-2. Under UCS-2,
  11349. therefore, the hexadecimal encoding for the letter 'a' is 0061 (Row 0, Column
  11350. 0x61). Row 0 of the BMP is essentially ISO 8859-1 (Latin-l) with the U.S.
  11351. dollar sign as the currency symbol.
  11352. ISO 10646 also defines combining characters, such as non-spacing diacritics.
  11353. In conforming applications, combining characters always follow the base
  11354. character that they modify. The UCS-2 encoding for Ã¡, then, consists of two
  11355. 16-bit integers: 0061 0301 (0301 is called the non-spacing acute). For
  11356. compatibility with existing character sets, there is also a single UCS-2 code
  11357. point for Ã¡ (00e1).
  11358. For the most part, only Roman characters have such dual representations. Some
  11359. non-Roman languages, such as Arabic, Hindi, and Thai, also require the use of
  11360. combining characters. ISO-10646 specifies three levels of conformance for
  11361. tools and applications:
  11362. Level 1 combining characters not allowed
  11363. Level 2 combining characters allowed for Arabic, Hebrew, and Indic scripts
  11364. only
  11365. Level 3 combining characters allowed with no restrictions
  11366.  
  11367.  
  11368. Unicode
  11369.  
  11370.  
  11371. Unicode is a 16-bit encoding scheme that supports most modern written
  11372. languages. It began independently of ISO 10646, but with Unicode version 1.1,
  11373. it is now a subset of 10646 (to be precise, it is UCS-2, Level 3). Unicode
  11374. also defines mapping tables to translate Unicode characters to and from most
  11375. national and international character set standards.
  11376. Some applications should readily convert to Unicode. Since ASCII is a subset,
  11377. it is only necessary to change narrow (eight-bit) characters to wide
  11378. characters. In C and C++, this means replacing char declarations with wchar_t.
  11379. Some other character sets, such as Thai and Hangul, appear in the same
  11380. relative order within Unicode, so you just need to add or subtract a fixed
  11381. offset. Converting Han characters requires a lookup table.
  11382. Vendors are now beginning to support Unicode, and tools are available at both
  11383. the operating system and API levels. Tools supporting the 32-bit encodings of
  11384. ISO 10646 are not expected for many years -- especially since no planes beyond
  11385. the BMP have been populated.
  11386.  
  11387.  
  11388. Bibliography
  11389.  
  11390.  
  11391. "UCS Coexistence/Migration," X/Open Internal Report, Doc. No. SC22/WG20 N252,
  11392. 1993.
  11393. The Unicode Standard. The Unicode Consortium, Addison-Wesley, 1991.
  11394. Katzner, Kenneth. The Languages of the World. 1986.
  11395. Martin, Sandra. "Internationalization Explored," UniForum, 1992.
  11396. Plauger, P. J., "Large Character Sets for C," Dr. Dobb's Journal, August 1992.
  11397. Figure 1 World Scripts
  11398. European
  11399.  Armenian, Cyrillic, Georgian, Greek, Roman
  11400. Indic
  11401.  Northern
  11402.  Bengali, Devanagari, Gujarati, Gurmukhi, Oriya
  11403.  Southern
  11404.  Kannada (Kanarese), Malayalam, Sinhalese, Tamil,
  11405.  Telugu
  11406.  Southeast
  11407.  Burmese, Khmer, Lao, Thai
  11408.  Central Asian
  11409.  Tibetan
  11410. Middle Eastern
  11411.  Arabic, Hebrew
  11412. East Asian (Oriental)
  11413.  Han, Bopomofo, Kana (Hiragana + Katakana), Hangul
  11414. Other Asian
  11415.  Lanna Thai, Mangyan, Mongolian, Naxi, Pollard, Pahawh
  11416.  Hmong, Tai Lü, Tai Nüa, Yi
  11417. African
  11418.  Ethiopian, Osmanya, Tifinagh, Vai
  11419. Native American
  11420.  Cree
  11421. Miscellaneous
  11422.  Chemistry, Mathematics, Publishing Symbols, IPA
  11423.  (International Phonetic Alphabet)
  11424.  
  11425. Figure 2 Eight-bit Character Set Standards
  11426. ISO 8859-1 (Latin-1) Western European
  11427. ISO 8859-2 (Latin-2) Eastern European
  11428. ISO 8859-3 (Latin-3) Southeastern European
  11429. ISO 8859-4 (Latin-4) Northern European
  11430. ISO 8859-5 Cyrillic
  11431. ISO 8859-6 Arabic
  11432. ISO 8859-7 Greek
  11433. ISO 8859-8 Hebrew
  11434. ISO 8859-9 (Latin-5) Western European + Turkish
  11435. Table 1 Standard C Headers: Group 1 (required knowledge for every C
  11436. programmer)
  11437. <ctype.h> Character Handling
  11438. <stdio.h> Input/Output
  11439. <stdlib.h> Miscellaneous Utilities
  11440. <string.h> Text Processing
  11441. Table 2 Standard C Headers: Group II (tools for the professional)
  11442. <assert.h> Assertion Support for
  11443.  Defensive Programming
  11444. <limits.h> System Parameters for
  11445.  Integer Arithmetic
  11446. <stddef.h> Universal Types &
  11447.  Constants
  11448. <time.h> Time Processing
  11449. Table 3 Standard C Headers: Group III (power at your fingertips when you need
  11450. it)
  11451. <errno.h> Error Detection
  11452. <float.h> System Parameters for
  11453.  Real Arithmetic
  11454. <locale.h> Cultural Adaptation
  11455. <math.h> Mathematical Functions
  11456. <setjmp.h> Non-local Branching
  11457. <signal.h> Interrupt Handling (sort of)
  11458. <stdarg.h> Variable-length Argument
  11459.  Lists
  11460. Table 4 Interger boundaries defined in <limits.h>
  11461. CHAR_BIT 8
  11462. SCHAR_MIN -127
  11463. SCHAR_MAX 127
  11464. UCHAR_MAX 255
  11465.  
  11466. [if char == signed char]
  11467. CHAR_MIN SCHAR_MIN
  11468. CHAR_MAX SCHAR_MAX
  11469. [else]
  11470. CHAR_MIN 0
  11471. CHAR_MAX UCHAR_MAX
  11472.  
  11473. MB_LEN_MAX 1
  11474.  
  11475. SHRT_MIN -32767
  11476. SHRT_MAX 32767
  11477. USHRT_MAX 65535
  11478.  
  11479. INT_MIN -32767
  11480. INT_MAX 32767
  11481. UINT_MAX 65535
  11482.  
  11483. LONG_MIN -2147483647
  11484. LONG_MAX 2147483647
  11485. ULONG_MAX 4294967295
  11486.  
  11487. Table 5 Definitions in <stddef.h>
  11488. ptrdiff_t type for pointer subtraction
  11489. size_t type for sizeof
  11490. wchar_t wide-character type
  11491. NULL zero pointer
  11492. offsetof offset in bytes of structure members
  11493. Table 6<stdlib.h> functions that use wchar_t
  11494. mbtowc translate multibyte character to wide character
  11495. wctomb translate wide character to multibyte character
  11496. mbstowcs translate multibyte string to wide string
  11497. wcstombs translate wide string to multibyte string
  11498. Table 7 Definitions in <time.h>
  11499.  Macros
  11500. ----------------------------------------------------
  11501. NULL
  11502. CLOCKS_PER_SEC
  11503.  
  11504.  Types
  11505. ----------------------------------------------------
  11506. size_t
  11507. clock_t system clock type
  11508. time_t ncoded time/date value
  11509. struct tm decoded time/date components
  11510.  
  11511.  Functions
  11512. ----------------------------------------------------
  11513. difftime duration between two times
  11514. mktime normalizes a struct tm
  11515. time retrieves current time encoding
  11516. asctime text representation of a time value
  11517. ctime text representation of current time
  11518. gmtime decodes into UTC time
  11519. localtime decodes a time value
  11520. strftime formats a decoded time
  11521.  
  11522. Listing 1 A use for CHAR_BIT
  11523. /* bit3.c: Toggle bits in a word */
  11524. #include <stdio.h>
  11525. #include <limits.h>
  11526.  
  11527. #define WORD unsigned int
  11528. #define NBYTES sizeof(WORD)
  11529. #define NBITS (NBYTES * CHAR_BIT)
  11530. #define NXDIGITS (NBYTES * 2)
  11531.  
  11532. main()
  11533. {
  11534. WORD n: 0;
  11535. int i, j;
  11536.  
  11537. for (j = 0; j < 2; ++j)
  11538. for (i = 0; i < NBITS; ++i)
  11539. {
  11540. n ^= (1 << i);
  11541. printf("%0*X\n",NXDIGITS,n);
  11542. }
  11543.  
  11544. return 0;
  11545. }
  11546.  
  11547.  
  11548. /* Output:
  11549. 0001
  11550. 0003
  11551. 0007
  11552. 000F
  11553. 001F
  11554. 003F
  11555. 007F
  11556. 00FF
  11557. 01FF
  11558. 03FF
  11559. 07FF
  11560. 0FFF
  11561. 1FFF
  11562. 3FFF
  11563. 7FFF
  11564. FFFF
  11565. FFFE
  11566. FFFC
  11567. FFF8
  11568. FFF0
  11569. FFE0
  11570. FFC0
  11571. FF80
  11572. FF00
  11573. FE00
  11574. FC00
  11575. F800
  11576. F000
  11577. E000
  11578. C000
  11579. 8000
  11580. 0000
  11581. */
  11582.  
  11583. /* End of File */
  11584.  
  11585.  
  11586. Listing 2 Uses <limits.h> to choose a suitable numeric type
  11587. /* range.c */
  11588. #include <stdio.h>
  11589. #include <limits.h>
  11590.  
  11591. #define LOWER_BOUND <your min here>
  11592. #define UPPER_BOUND <your max here>
  11593.  
  11594. /* Determine minimal numeric type for range */
  11595. #if LOWER_BOUND < LONG_MIN LONG_MAX < UPPER_BOUND
  11596. typedef double Num_t;
  11597. #elif LOWER_BOUND < INT_MIN INT_MAX < UPPER_BOUND
  11598. typedef long Num_t;
  11599. #elif LOWER_BOUND < SCHAR_MIN SCHAR_MAX < UPPER_BOUND
  11600. typedef int Num_t;
  11601. #else
  11602. typedef signed char Num_t;
  11603. #endif
  11604.  
  11605. main()
  11606.  
  11607. {
  11608. Num_t x;
  11609.  
  11610. printf("sizeof(Num_t) == %d\n",sizeof x);
  11611. return 0;
  11612. }
  11613.  
  11614. /* End of File */
  11615.  
  11616.  
  11617. Listing 3 Illustrates wide character strings
  11618. #include <stddef.h>
  11619. #include <stdio.h>
  11620.  
  11621. main()
  11622. {
  11623. char str[] = "hello";
  11624. wchar_t wcs[] = L"hello";
  11625.  
  11626. printf("sizeof str = %d\n",sizeof str);
  11627. printf("sizeof wcs = %d\n",sizeof wcs);
  11628. return 0;
  11629. }
  11630.  
  11631. /* Output:
  11632. sizeof str = 6
  11633. sizeof wcs = 12
  11634. */
  11635. /* End of File */
  11636.  
  11637.  
  11638. Listing 4 offsetof exposes alignment within a structure
  11639. #include <stddef.h>
  11640. #include <stdio.h>
  11641.  
  11642. struct Person
  11643. {
  11644. char name[15];
  11645. int age;
  11646. };
  11647.  
  11648. main()
  11649. {
  11650. printf("%d\n",offsetof(struct Person, age));
  11651. return 0;
  11652. }
  11653.  
  11654. /* Output:
  11655. 16
  11656. /*
  11657.  
  11658. / End of File */
  11659.  
  11660.  
  11661. Listing 5 Stopwatch Function Prototypes
  11662. /* timer.h: Stopwatch Functions */
  11663.  
  11664. void timer_reset(void);
  11665. void timer_wait(double nsecs);
  11666.  
  11667. double timer_elapsed(void);
  11668.  
  11669. /* End of File */
  11670.  
  11671.  
  11672. Listing 6 Implementation of Stopwatch Functions
  11673. /* timer.c: Stopwatch Functions */
  11674.  
  11675. #include <time.h>
  11676. #include "timer.h"
  11677.  
  11678. static clock_t start = (clock_t) 0;
  11679.  
  11680. /* Reset the timer */
  11681. void timer_reset(void)
  11682. {
  11683. start = clock();
  11684. }
  11685.  
  11686. /* Wait a number of seconds */
  11687. void timer_wait(double secs)
  11688. {
  11689. clock_t stop = clock() +
  11690. (clock_t) (secs* CLOCKS_PER_SEC);
  11691. while (clock() < stop);
  11692. ;
  11693. }
  11694.  
  11695. /* Compute elapsed time in seconds */
  11696. double timer_elapsed(void)
  11697. {
  11698. return (double)(clock() - start) / CLOCKS_PER_SEC;
  11699. }
  11700.  
  11701. /* End of File */
  11702.  
  11703.  
  11704. Listing 7 Illustrates the stopwatch functions
  11705. /* t_timer.c: Tests the stopwatch functions */
  11706.  
  11707. #include <stdio.h>
  11708. #include <limits.h>
  11709. #include "timer.h"
  11710.  
  11711. main()
  11712. {
  11713.  
  11714. long i;
  11715.  
  11716. timer_reset();
  11717.  
  11718. /* Delay */
  11719. for (i = 0; i < LONG_MAX; ++i)
  11720. ;
  11721.  
  11722. /* Get elapsed time */
  11723. printf("elapsed time: %lf secs\n", timer_elapsed());
  11724. return 0;
  11725. }
  11726.  
  11727.  
  11728. /* Output:
  11729. elapsed time: 565.070000 secs
  11730. */
  11731.  
  11732. /* End of File */
  11733.  
  11734.  
  11735. Listing 8 The definition of struct tm from <time.h>
  11736. struct tm
  11737. {
  11738. int tm_sec; /* seconds (0 - 59) */
  11739. int tm_min; /* minutes (0 - 59) */
  11740. int tm_hour; /* hours (0 - 23) */
  11741. int tm_mday; /* day of month (1 - 31) */
  11742. int tm_mon; /* month (0 - 11) */
  11743. int tm_year; /* years since 1900 */
  11744. int tm_wday; /* day of week (0 - 6) */
  11745. int tm_yday; /* day of year (0 - 365) */
  11746. int tm_isdst; /* daylight savings flag */
  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. Stepping Up To C++
  11791.  
  11792.  
  11793. Minor Enhancements to C++ as of CD Registration
  11794.  
  11795.  
  11796.  
  11797.  
  11798. Dan Saks
  11799.  
  11800.  
  11801. Dan Saks is the president of Saks & Associates, which offers consulting and
  11802. training in C++ and C. He is secretary of the ANSI and ISO C++ committees. Dan
  11803. is coauthor of C++ Programming Guidelines, and codeveloper of the Plum Hall
  11804. Validation Suite for C++ (both with Thomas Plum). You can reach him at 393
  11805. Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601, or
  11806. electronically at dsaks@wittenberg.edu.
  11807.  
  11808.  
  11809. In July, the ANSI and ISO C++ standards committees voted to submit their
  11810. working draft of the C++ standard for registration as a CD (committee draft).
  11811. This is the second installment in a series that summarizes the state of the
  11812. C++ language as of that milestone.
  11813. Since adopting the ARM [1] as the base document for the standard, the
  11814. committees have done a lot more than just clarify the C++ language definition;
  11815. they have been changing existing features and adding new ones at a fairly
  11816. steady clip. CD registration represents, among other things, a tacit statement
  11817. from the committees that they're more or less done adding new stuff. The words
  11818. in the draft are not yet as clear or as precise as they will be when the
  11819. standard is final, but at least now the draft describes most, if not all, of
  11820. the language features we should expect to see in that eventual C++ standard.
  11821. Last month, I listed all of the ways the C++ language described by the draft
  11822. standard differs from the dialect described in the ARM. (See "Stepping Up to
  11823. C++: C++ at CD Registration," CUJ, January 1995.) My list included only
  11824. substantive changes (changes in the C++ language itself) and omitted changes
  11825. that are essentially editorial (changes in the description of C++, not in C++
  11826. itself).
  11827. I classified the substantive changes into four broad categories:
  11828. major extensions that dramatically increase the complexity of the language and
  11829. support alternative programming styles and paradigms
  11830. minor enhancements that extend the language in less dramatic ways
  11831. changes that alter the meaning of existing features
  11832. clarifications of existing features
  11833. I also provided brief explanations of each of the major extensions:
  11834. templates
  11835. exception handling
  11836. run-time type information (including dynamic_cast)
  11837. namespaces
  11838. along with pointers to other sources of information about these features.
  11839. This month, I continue in the same vein with explanations of the minor
  11840. enhancements. Some of these review features I described in earlier columns.
  11841. (See "Stepping Up To C++: Recent Extensions to C++," CUJ, June, 1993, and
  11842. "Stepping Up To C++: The Return Type of Virtual Functions," CUJ, March, 1994.)
  11843. Stroustrup [2] offers more insights and examples on most of these features.
  11844.  
  11845.  
  11846. New Keywords and Digraphs
  11847.  
  11848.  
  11849. C++, like C, requires a character set based on the Roman alphabet. In
  11850. particular, C++ uses the English alphabetic characters 'A' through 'Z' and 'a'
  11851. through 'z', along with a handful of arithmetic operators and punctuation
  11852. symbols. Unfortunately, many other natural languages based on the Roman
  11853. alphabet use more than just the 26 (times two) characters in the English
  11854. alphabet.
  11855. Computer systems that support non-English alphabets usually make room for
  11856. native language characters and punctuation by omitting other punctuation
  11857. characters normally found on American English systems. For example, Danish
  11858. keyboards, displays, and printers replace the [ and ] with Ã† and Ã…,
  11859. respectively. A subscript expression like a[i] comes out on a Danish display
  11860. device looking like aÆiÃ…. I can imagine that this takes some of the fun out of
  11861. programming in C and C++.
  11862. ISO646 is the international standard 7-bit character set. It specifies the set
  11863. of characters common to all systems based on the Roman alphabet. ISO646 does
  11864. not use every available 7-bit code. Each nationality can define its own
  11865. variant of ISO646 that uses the unused codes for native language characters or
  11866. symbols. ASCII, the US national variant of ISO646, uses the available codes
  11867. for symbols like {, }, [, ], /, ^, \, and ~ (all of which are used in C and
  11868. C++). Other national variants assign these same codes to other alphabetic
  11869. characters, like Ã  or Ã¼, or to punctuation like Â¿.
  11870. A current trend in international standards is to augment programming languages
  11871. so that programmers can work without too much inconvenience using only the
  11872. invariant set of ISO646 characters. As part of this trend, C++ includes new
  11873. keywords and digraphs (two-character symbols) as alternate ISO646-compliant
  11874. spellings for the troublesome operators. These keywords and digraphs appear in
  11875. Table 1. Using the digraphs, you can write the subscripting expression a[i] as
  11876. a<:i:>.
  11877. ISO C provides the same alternate spellings; however, C specifies the
  11878. identifiers in Table 1 as macros defined in a standard header <iso646.h>.
  11879. Thus, you can continue using those identifiers as user-defined identifiers in
  11880. C as long as you don't include <iso646.h>. However, C++ treats these
  11881. identifiers as reserved words; you cannot use them as user-defined identifiers
  11882. in any C++ program. The C++ library provides a header <iso646.h> (which may be
  11883. empty) just for compatibility with C.
  11884.  
  11885.  
  11886. Operator Overloading on Enumerations
  11887.  
  11888.  
  11889. At one time, enumerations were integral types (as they are in C), but those
  11890. days are gone. The current draft says that each distinct enumeration
  11891. constitutes a different enumerated type. Enumerations are not integral, but
  11892. they do promote to int, unsigned int, long, or unsigned long.
  11893. For example, given
  11894. enum day { SUN, MON, ..., SAT };
  11895. a valid C declaration like
  11896. enum day d = 0;
  11897. is no longer valid in C++, because C++ won't convert 0 (an int) to day (an
  11898. enumeration) without a cast. However,
  11899. int n = SUN;
  11900. is still valid in C++, as it is in C, because SUN (a constant of type day)
  11901. still promotes to int.
  11902. C++ does not allow operator overloading on primitive types, such as int. When
  11903. enumerations were integral types, C++ could not allow operator overloading on
  11904. enumerations either, without severely complicating the rules for overload
  11905. resolution. Once enumerations became distinct types, allowing overloading on
  11906. enumerations became a relatively small change.
  11907. Removing enumerations from the integral types introduced a potentially serious
  11908. incompatibility with C, namely, that various built-in arithmetic operators,
  11909. such as ++, --, += and -=, no longer apply to enumerations. Therefore, a loop
  11910. such as
  11911. for (d = SUN; d <= SAT; ++d)
  11912. no longer compiles as C++ when ++ is the predefined operator. However, with
  11913. operator overloading on enumerations, you can define
  11914. inline day &operator++(day &d)
  11915.  
  11916. {
  11917. return d = day(d + 1);
  11918. }
  11919. as the prefix form of ++ for objects of type day. With this function
  11920. definition present, the for statement will compile.
  11921.  
  11922.  
  11923. operator new[] and operator delete[]
  11924.  
  11925.  
  11926. A new expression acquires storage by calling an allocation function. The
  11927. standard C++ library provides a default allocation function, declared as
  11928. void *operator new(size_t n);
  11929. An expression such as
  11930. px = new X;
  11931. loosely translates into code of the form
  11932. px = (X *)operator new(sizeof(X));
  11933. px->X(); // apply constructor
  11934. A delete expression releases storage by calling a deallocation function. The
  11935. default deallocation function declared in the library has the form
  11936. void operator delete(void *p);
  11937. An expression such as
  11938. delete px;
  11939. loosely translates into code of the form
  11940. px->~X(); // apply destructor
  11941. operator delete(px);
  11942. The default allocation and deallocation functions are very general, and may
  11943. waste too much time or storage in some applications. C++ lets you write you
  11944. own versions of operator new and operator delete. If you define your own
  11945. operator new, every new expression in your program will call your allocator
  11946. instead of the default supplied in the library. Ditto for operator delete and
  11947. delete expressions.
  11948. Writing replacements for the global allocator and deallocator can be a big
  11949. chore, and it's usually unnecessary. Often all you need to do is tune the
  11950. memory allocator for objects of a few particular classes. C++ lets you define
  11951. a different allocator and deallocator for each class. For example,
  11952. class node
  11953. {
  11954. public:
  11955. void *operator new(size_t);
  11956. void operator delete(void *p);
  11957. ...
  11958. };
  11959. defines class node with its own class-specific versions of new and delete. A
  11960. call such as
  11961. p = new node;
  11962. allocates memory using node::operator new, rather than the global operator
  11963. new. Similarly,
  11964. delete p;
  11965. deallocates memory using node::operator delete. new and delete expressions for
  11966. objects of built-in types, and for class types that do not declare their own
  11967. operator new and operator delete, continue to use the global allocation and
  11968. deallocation functions.
  11969. According to the ARM, allocating an array of X objects always uses the global
  11970. operator new, even if X is a class that defines its own operator new. That is,
  11971. px = new X[n];
  11972. ignores X::operator new and uses ::operator new. By the same token, deleting
  11973. that array using
  11974. delete [] px;
  11975. ignores X::operator delete and uses ::operator delete.
  11976. The current C++ draft standard gives programmers greater control over dynamic
  11977. memory management for arrays of objects. Now, a new expression such as
  11978. px = new X[n];
  11979. invokes an allocation function declared as
  11980. void *operator new[](size_t n);
  11981. Similarly,
  11982. delete [] px;
  11983. invokes a deallocation function declared as
  11984. void operator delete[](void *p);
  11985. The standard library provides default implementations for operator new[] and
  11986. operator delete[], which you can replace at your pleasure. You can even define
  11987. operators new[] and delete[] for individual classes, as in
  11988. class node
  11989. {
  11990. public:
  11991. void *operator new(size_t);
  11992. void *operator new[](size_t);
  11993. void operator delete(void *p);
  11994. void operator delete[](void *p);
  11995. ...
  11996.  
  11997. };
  11998. so that
  11999. p = new node[n];
  12000. invokes node::operator new[] instead of node::operator new.
  12001.  
  12002.  
  12003. Relaxed Virtual Function Return Typ es
  12004.  
  12005.  
  12006. Quoth the ARM: "It is an error for a derived class function to differ from a
  12007. base class virtual function in the return type only." For example, in
  12008. class B
  12009. {
  12010. public:
  12011. virtual int vf(int);
  12012. ...};
  12013. class D : public B
  12014. {
  12015. ...
  12016. void vf(int); // error
  12017. ...
  12018. };
  12019. the declaration of D::vf is an error because it has the same name and
  12020. signature (parameter types) as a function declared in base class B, but they
  12021. have different return types. Most of the time, this is a reasonable
  12022. restriction. Occasionally it isn't. Here's one such occasion.
  12023. Some applications need to be able to clone (create an exact copy of) an
  12024. object. Typically, a cloning function for a class X looks something like
  12025. X *X::clone() const
  12026. {
  12027. return new X(*this);
  12028. }
  12029. The expression new X(*this) creates a new dynamically-allocated X object by
  12030. copying the object addressed by this.
  12031. When used in a class that's the root of a polymorphic hierarchy, clone
  12032. functions should be virtual (possibly pure), so you can clone an object
  12033. without knowing its exact type. For example, Listing 1 shows a simple
  12034. hierarchy of geometric shapes with a virtual clone function. Using these
  12035. types, a call such as
  12036. shape *cs = s->clone();
  12037. creates a new shape with the same dynamic type as shape s. That is, if at the
  12038. time of the call, s actually points to a circle, then s->clone() returns a
  12039. circle *. If s points to a rectangle, then s->clone() returns a rectangle *.
  12040. Normally, a clone function for a class X should have a return type of X *.
  12041. However, in Listing 1, circle::clone returns a shape *, not a circle *, to
  12042. satisfy the ARM's restriction that an overriding function must return exactly
  12043. the same type as the function it overrides. Unfortunately, satisfying this
  12044. restriction often requires you to use casts in contexts where it seems that
  12045. you should not need them. For example, you cannot write
  12046. rectangle *r;
  12047. ...
  12048. rectangle *cr = r->clone();
  12049. because r->clone() returns a shape *, not a rectangle *. Even though a
  12050. rectangle is a shape, a shape is not necessarily a rectangle. Therefore, you
  12051. must add a cast, as in
  12052. rectangle *cr = (rectangle *)r->clone();
  12053. The current draft relaxes the ARM's original restriction to eliminate the need
  12054. for this cast. You can declare each virtual clone function in the hierarchy so
  12055. that it returns a pointer whose static type is the same as its dynamic type.
  12056. That is, circle::clone can return a circle * and rectangle::clone can return a
  12057. rectangle *, even though the function they override, shape::clone, returns a
  12058. shape *.
  12059. By and large, the ARM's restriction still stands. The current draft simply
  12060. adds two special cases where the return types need not match exactly.
  12061. Specifically, for all classes B and D defined as
  12062. class B
  12063. {
  12064. ...
  12065. virtual BT f();
  12066. ...
  12067. };
  12068. class D : public B
  12069. {
  12070. ...
  12071. DT f();
  12072. ...
  12073. };
  12074. types BT and DT must be identical, or they must satisfy either of the
  12075. following conditions:
  12076. 1. BT is BB * and DT is DD *, where DD is derived from BB.
  12077. 2. BT is BB & and DT is DD &, where DD is derived from BB.
  12078. In either case (1) or (2),
  12079. 3. class D must have the access rights to convert a BB * (or BB &) to a DD *
  12080. (or DD &, respectively).
  12081. In most applications with hierarchies like this, BB is a public base class of
  12082. DD, so D can perform the conversions. But if, for example, BB is a private
  12083. base class of DD, then condition (3) does not hold. The above rules apply even
  12084. if D is derived indirectly from B. Furthermore, BB might be B and DD might be
  12085. D, as is the case with clone functions.
  12086. Types BB and DD, above, may include cv-qualifiers (const and volatile). These
  12087. qualifiers in BB and DD need not be identical, as long as every qualifier
  12088. that's part of DD is also part of BB. BB may have qualifiers that are not part
  12089. of DD.
  12090.  
  12091.  
  12092.  
  12093. wchar_t as a Distinct Type
  12094.  
  12095.  
  12096. Like C, C++ provides wide-character literals and multibyte strings so that
  12097. programmers can manipulate very large character sets, like Japanese Kanji.
  12098. Several headers in the Standard C library define wchar_t as the wide character
  12099. type, an integral type sufficiently large to represent all character codes in
  12100. the largest character set among the supported locales.
  12101. The C++ committees wanted to support input/output for wide characters as well
  12102. as "narrow" characters. For example, for a time they wanted to be able to add
  12103. another output operator
  12104. ostream &operator<<(ostream &os,
  12105. wchar_t w);
  12106. in addition to existing operators, such as
  12107. ostream &operator<<(ostream &os,
  12108. char c);
  12109. ostream &operator<<(ostream &os,
  12110. int i);
  12111. //and many others
  12112. so that given
  12113. int i;
  12114. wchar_t w;
  12115. the expression
  12116. cout << i;
  12117. displays i as an int, and
  12118. cout << w;
  12119. displays w in its proper graphic representation. Unfortunately, this was
  12120. impossible with wchar_t defined by a typedef.
  12121. The problem is that a typedef is not a distinct type; it's an alias for
  12122. another type. If the library defines wchar_t as
  12123. typedef wchar_t int;
  12124. then
  12125. ostream &operator<<(ostream &os,
  12126. int i);
  12127. ostream &operator<<(ostream &os,
  12128. wchar_t w);
  12129. are the same function. That function will (most likely) display objects of
  12130. type wchar_t as numbers.
  12131. Thus, wchar_t is now a keyword in C++ representing a distinct type. wchar_t
  12132. still has the same representation as one of the standard integral types
  12133. (meaning it has the same size, alignment requirements, and "signedness" as one
  12134. of the integral types), but it is now a distinct type for the purposes of
  12135. overload resolution.
  12136.  
  12137.  
  12138. A Built-in Boolean Type
  12139.  
  12140.  
  12141. C doesn't have a Boolean type; it simply assumes that a scalar value is
  12142. "false" if it compares equal to zero and "true" if it's non-zero. For the most
  12143. part, the Standard C library uses int as the Boolean type, and many
  12144. programmers follow suit.
  12145. Those of us who truly felt the absence of a Boolean type defined one
  12146. ourselves. Unfortunately, there are an awful lot of different ways to do it.
  12147. Some programmers spell the type as bool. Others use Bool, boolean, Boolean,
  12148. bool_t, or even logical. The type can be char or int, signed or unsigned. The
  12149. definition itself can be a macro
  12150. #define bool int
  12151. #define false 0
  12152. #define true 1
  12153. or a typedef
  12154. typedef unsigned char bool;
  12155. or even an enumeration
  12156. enum bool { false, true };
  12157. Consequently, it's not at all uncommon for the definitions of bool, false, and
  12158. true (however you spell them) in one library to conflict with the definitions
  12159. in another.
  12160. The C++ standards committees decided to eliminate these conflicts by defining
  12161. a standard Boolean type. C++ now has a Boolean type with the following
  12162. properties:
  12163. bool is a keyword designating a signed integral type.
  12164. false and true are keywords designating constants of type bool.
  12165. bool expressions promote to int such that false converts to zero and true
  12166. converts to one.
  12167. int and pointer expressions can convert to bool such that zero converts to
  12168. false and non-zero converts to true.
  12169. In addition:
  12170. The built-in relational operators <, >, ==, !=, <=, and >= yield a bool
  12171. result.
  12172. The built-in logical operators &&, //, and ! take bool operands (or operands
  12173. that automatically convert to bool) and yield a bool result.
  12174. The conditional expression in an if, while, or for statement, or the first
  12175. operand of ?:, must have bool type (or a type that automatically converts to
  12176. bool).
  12177. The committees' intent is that, except for definitions of bool, false, and
  12178. true, code that uses a Boolean type should continue to work as before.
  12179. The committees agreed to preserve, at least for the time being, the sloppy but
  12180. apparently common coding practice of using ++ to set a Boolean value to true.
  12181. C++ currently accepts prefix and postfix ++ as lvalue, of type bool, both of
  12182. which set to true. However, the draft deprecates this feature, meaning it may
  12183. disappear from some future standard.
  12184. By the way, the committees did explore alternatives to defining bool as a
  12185. built-in type. They considered defining it in the library as a typedef, as a
  12186. class, or as an enumeration, but each approach had flaws.
  12187. The problem with a typedef is that it is not a distinct type for overloading.
  12188. If the library defines
  12189.  
  12190. typedef int bool;
  12191. then declarations
  12192. void f(int);
  12193. void f(bool);
  12194. are not a pair of overloaded functions; they're two declarations for the same
  12195. function. (This is the same reason why wchar_t is not a typedef.)
  12196. Defining bool as a class type poses a different problem. Given the current
  12197. rules for applying user-defined conversions during argument-matching, defining
  12198. bool as a class might break existing code. In their analysis of the Boolean
  12199. type, Dag Brück and Andrew Koenig used an example similar to the one in
  12200. Listing 2 to illustrate the problem.
  12201. Listing 1 shows a class X with a constructor that accepts a single int
  12202. argument. Thus, that constructor is also a user-defined conversion from int to
  12203. X. In the absence of a bool type, the expression n > 0 yields an int. The call
  12204. f(n > 0) uses the constructor X(int) to convert that int result to X, and then
  12205. passes that X to f(X).
  12206. Now suppose C++ defined bool as a class type with a conversion to int:
  12207. class bool
  12208. {
  12209. public:
  12210. operator int();
  12211. ...
  12212. };
  12213. If relational operators, like >, yield a bool result, then the call f(n > 0)
  12214. in Listing 2 must first convert the result of n > 0 to int using
  12215. bool::operator int, and then convert that int to X using X::X(int).
  12216. Unfortunately, there's already a rule in C++ that argument matching during a
  12217. function call will apply at most one user-defined conversion. When bool is a
  12218. class, this call requires two user-defined conversions: bool::operator int and
  12219. X::X(int). Brück and Koenig reasoned that to avoid breaking this code or
  12220. meddling with the already complicated argument-matching rules, the conversion
  12221. from bool to int cannot be user-defined; it must be built-in.
  12222. When enumerations were still integral types, it might have been possible to
  12223. define bool as an enumeration and preserve the implicit conversions from int
  12224. to bool. Since int values no longer convert quietly to enumerations, it can't
  12225. be done without special rules for bool. But that's the same as making bool a
  12226. built-in type.
  12227.  
  12228.  
  12229. Declarations in Conditional Expressions
  12230.  
  12231.  
  12232. The proposal to add run-time type information (RTTI) included an extension to
  12233. allow declarations in conditional expressions. The scope of a name declared in
  12234. a condition is the statement(s) controlled by that condition. For example,
  12235. if (circle *c = dynamic_cast<circle *>(s))
  12236. {
  12237. // c is in scope here...
  12238. }
  12239. // but not here
  12240. The declaration yields the value of the declared object after initialization.
  12241. The syntax for such declarations is extremely limited. (The revised grammar
  12242. appears in Table 2.) The declaration can declare only one object, and it must
  12243. have an initializer with = as the delimiter. For example, C++ will not accept
  12244. if (complex z(r, i))
  12245. You must write the condition as
  12246. if (complex z = complex(r, i))
  12247. Declarations can appear in place of expressions only in the controlling
  12248. expressions of if, switch, for, or while statements. They cannot appear in the
  12249. increment expression of a for statement, in the controlling expression of a
  12250. do-while statement, or in the conditional expression of ?: expression.
  12251. The scope of a name declared in the condition of an if statement includes the
  12252. else part. For example,
  12253. if (int c = getchar())
  12254. {
  12255. // c is non-zero here
  12256. }
  12257. else
  12258. {
  12259. // c is zero here
  12260. }
  12261. //c is undefined here
  12262. Finally, you cannot declare an entity in the outermost block of the statement
  12263. controlled by a condition using the same name as the object declared in the
  12264. condition. For example,
  12265. while (X *p = iter.next())
  12266. {
  12267. char *p; // error, p already declared
  12268. ...
  12269. }
  12270. I'll continue next month with the explanations of the other minor enhancements
  12271. to C++.
  12272. References
  12273. [1] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference
  12274. Manual. (Addison-Wesley, 1990).
  12275. [2] Bjarne Stroustrup. The Design and Evolution of C++. (Addison-Wesley,
  12276. 1994).
  12277. Table 1 New keywords and digraphs keywords as alternate ISO646- compliant
  12278. spellings for existing operators.
  12279. Existing Alternate
  12280. Spelling Spelling
  12281. ------------------
  12282.  [ <:
  12283.  ] :>
  12284.  
  12285.  { <%
  12286.  } %>
  12287.  # %:
  12288.  ## %:%:
  12289.  & bitand
  12290.  && and
  12291.  bitor
  12292.  or
  12293.  ^ xor
  12294.  ~ compl
  12295.  &= and_eq
  12296.  = or_eq
  12297.  ^= xor_eq
  12298.  ! not
  12299.  != not_eq
  12300. Table 2 Grammar changes to allow declarations in conditionals
  12301.  Old grammars (as in the ARM):
  12302.  
  12303. selection-statement:
  12304.  if ( expression ) statement
  12305.  if ( expression ) statement else statement
  12306.  switch ( expression ) statement
  12307.  
  12308. iteration-statement:
  12309.  while ( expression ) statement
  12310.  do statement while ( expression ) ;
  12311.  for ( for-init-statement expressionopt ; expressionopt ) statement
  12312.  
  12313.  New grammar (as in the draft):
  12314.  
  12315. condition:
  12316.  expression
  12317.  type-specifier-seq declarator = assignment-expression
  12318.  
  12319. selection-statement:
  12320.  if ( condition ) statement
  12321.  if ( condition ) statement else statement
  12322.  switch ( condition ) statement
  12323.  
  12324. iteration-statement:
  12325.  while ( condition ) statement
  12326.  do statement while ( expression ) ;
  12327.  for ( for-init-statement conditionopt ; expressionopt ) statement
  12328.  
  12329. Listing 1 A polymorphic hierarchy of shapes with a clone function
  12330. class shape
  12331. {
  12332. public:
  12333. virtual shape *clone() const = 0;
  12334. ...
  12335. };
  12336.  
  12337. class circle : public shape
  12338. {
  12339. public:
  12340. shape *clone() const;
  12341. ...
  12342. };
  12343.  
  12344.  
  12345. shape *circle::clone() const
  12346. {
  12347. return new circle(*this);
  12348. }
  12349.  
  12350. class rectangle : public shape
  12351. {
  12352. public:
  12353. shape *clone() const;
  12354. ...
  12355. };
  12356.  
  12357. shape *rectangle::clone() const
  12358. {
  12359. return new rectangle(*this);
  12360. }
  12361.  
  12362.  
  12363. Listing 2 An example that illustrates why bool can't be a class type
  12364. class X
  12365. {
  12366. public:
  12367. X(int);
  12368. ...
  12369. };
  12370.  
  12371. void f(X);
  12372.  
  12373. int main()
  12374. {
  12375. ...
  12376. f(n >0);
  12377. ...
  12378. }
  12379.  
  12380.  
  12381.  
  12382.  
  12383.  
  12384.  
  12385.  
  12386.  
  12387.  
  12388.  
  12389.  
  12390.  
  12391.  
  12392.  
  12393.  
  12394.  
  12395.  
  12396.  
  12397.  
  12398.  
  12399.  
  12400.  
  12401.  
  12402.  
  12403.  
  12404.  
  12405.  
  12406.  
  12407.  
  12408. On The Networks
  12409.  
  12410.  
  12411. Where to Get the Source
  12412.  
  12413.  
  12414.  
  12415.  
  12416. Sydney S. Weinstein
  12417.  
  12418.  
  12419. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, lecturer, author,
  12420. professor, and President of Myxa Corporation, an Open Systems Technology
  12421. company specializing in helping companies move to and work with Open Systems.
  12422. He can be contacted care of Myxa Corporation, 3837 Byron Road, Huntingdon
  12423. Valley, PA 19006-2320, or via electronic mail using the Internet/USENET
  12424. mailbox syd@Myxa.com (dsinc!syd for those that cannot do Internet addressing).
  12425.  
  12426.  
  12427. This is my fifth anniversary in writing this column, and before I go on to
  12428. some highlights, I want to update you on where to get the sources mentioned in
  12429. this column. One method I can tell you won't work is e-mailing me for it. I
  12430. write these columns based on the CUJ production schedule, which is months
  12431. ahead of when you see them. So the items I write about are long since gone
  12432. from my local disk before you even know I have written about them.
  12433. However, all is not lost. Many Internet sites do archive postings that were
  12434. made to the USENET Network News source groups. And with the increasing
  12435. availability of Internet access, its becoming even easier to reach them.
  12436. First, the quick overview: "On The Networks" covers articles posted to several
  12437. of the source groups on USENET Network News: comp.sources.games,
  12438. comp.sources.misc, comp.sources.reviewed, comp.sources.unix, comp.sources.x,
  12439. and alt.sources. (However, comp.sources.reviewed is a bit quiet of late.) Each
  12440. of these groups is like a section of a large electronic magazine called USENET
  12441. Network News. I call it a magazine, and not a bulletin board, partly because
  12442. of the way its "news" is distributed. Unlike a bulletin board, requiring each
  12443. reader to access a central machine to read the messages, USENET Network News
  12444. arrives on a subscription basis at each participating computer site.
  12445. Since most participating sites keep news articles online for a short period of
  12446. time (usually less than two weeks), by the time a piece of software appears in
  12447. this column it will have been expired and deleted for a long time. Thus it is
  12448. necessary to access a news archive site.
  12449. Many sites around the country have agreed to archive specific news groups.
  12450. These archive sites are listed in the comp.archives.news group. Many archive
  12451. sites also identity themselves as such in their USENET Mapping Project map
  12452. entry. Some sites have even been listed in this column. These sites allow
  12453. remote computers to access their archives to retrieve the sources. How you
  12454. access the archives depends on where they are, and how that site has set up
  12455. access. Most archives are available for either FTP (file transfer protocol, a
  12456. TCP/IP protocol used by internet sites) or UUCP access and a few even allow
  12457. both.
  12458. If a site supports FTP access, you need to be on the Internet to access them.
  12459. FTP allows for opening up a direct connection to the FTP server on their
  12460. system and transferring the files directly to your system. FTP will prompt for
  12461. a user name and optionally a password. Most FTP archive sites allow the user
  12462. name anonymous. FTP may then prompt for a password; any password will work,
  12463. but convention and courtesy dictate that you use your name and site address
  12464. for the password.
  12465. Recently CompuServe and AOL have announced plans to provide FTP support,
  12466. however there are already many local Internet access providers across the
  12467. country. I've seen a rash of books lately that list various Internet
  12468. providers. A trip to the local computer bookstore should provide you with a
  12469. list from which to start your search.
  12470. If a site supports UUCP access, anyone with UUCP can access the archives. Most
  12471. sites of this type publish a sample entry for the Systems (L. sys) file
  12472. showing the system name, phone number of their modems, the connection speeds
  12473. supported, and the login sequence. Using the uucp command you can poll the
  12474. system directly and retrieve the software. Many sites post time-of-day
  12475. restrictions on access to their modems. Courtesy dictates that you follow
  12476. their requests, and some sites use programs to enforce the limits. Be sure to
  12477. call early enough to complete your transfer in the allotted time.
  12478.  
  12479.  
  12480. How to Decide What to Retrieve
  12481.  
  12482.  
  12483. When I list a package archived in the moderated groups I provide five pieces
  12484. of information for each package: the Volume, Issue(s), archive name, the
  12485. contributor's name, and their electronic mail address. My listing will
  12486. explicitly name the volume and issue numbers. I show the archive name in
  12487. italics, and list the contributor's name followed by their electronic mail
  12488. address, in angle brackets (<>).
  12489. To locate a package via WAIS or archie, use the archive name -- the short,
  12490. one-word name in italics shown with each listing. To find the file at an
  12491. archive site, use the group name (listed in this column's section header), the
  12492. volume name, and the archive name. Most archive sites store the postings as
  12493. group/volume/archivename. The issue numbers tell you how many parts a package
  12494. was split into when posted. You can check the issue numbers to be sure you get
  12495. all of the parts.
  12496. I also report on patches to prior postings. These patch listings also include
  12497. the volume, issue(s), archive name, the contributor's name, and their
  12498. electronic mail address. Different archive sites store patches differently.
  12499. Some sites store patches along with the original volume/archive name of the
  12500. master posting. Some sites store them by the volume/archive name of the patch
  12501. itself. The archive name listed is always the same for both the patch and the
  12502. original posting.
  12503. alt. sources, being unmoderated, does not have volume and issue numbers. So I
  12504. report on the date in the "Date:" header of the posting and the number of
  12505. parts in which it appeared. If the posting was assigned an archive name by the
  12506. contributor, I also report on that archive name. Archive sites for alt.sources
  12507. are harder to find, but they usually store things by the archive name.
  12508.  
  12509.  
  12510. Where to Retrieve It
  12511.  
  12512.  
  12513. Let's say you've read this column, and you've figured out what you want, and
  12514. how to specify it in terms of archive name, group, volume name, etc. The
  12515. problem then is finding out which sites archive which groups, and how to
  12516. access these archives. I again refer to the articles by Kent Landfield of
  12517. Sterling Software and Jonathan I. Kames of the Massachusetts Institute of
  12518. Technology, posted to comp.sources.wanted and news.answers. These articles
  12519. appear weekly and explain how to find sources.
  12520. As a quick review, here are the steps:
  12521. I. If you know which sites archive the group, derive the path name for the
  12522. item. Most archive sites keep their information in a hierarchy ordered first
  12523. on the group, then on the volume, and last on the archive name. These together
  12524. usually make up a directory path, as in comp.sources.unix/volume22/elm2.3. In
  12525. that directory you will find all of the articles that made up the 2.3 release
  12526. of the Elm Mail User Agent that was posted in Volume 22 of the
  12527. comp.sources.unix newsgroup. If you do not know the archive name, but do know
  12528. the volume, each volume also contains an index file that can be retrieved and
  12529. read to determining the archive name. One common publicly accessible archive
  12530. site for each of the moderated groups in this column is UUNET (ftp.uu.net).
  12531. II. If you do not know which sites archive the groups, or whether any site is
  12532. archiving a particular item, (because they are not archiving the entire
  12533. group), consult archie. (See "Archie At Your Service," CUJ, August 1991, Vol.
  12534. 9, No. 8). archie is a mail response program that tries to keep track of sites
  12535. reachable via FTP that have sources available for distribution. Even if you
  12536. cannot access the archive site directly via FTP, it is worth knowing that the
  12537. archive site exists because there are other ways of retrieving sources
  12538. available via FTP.
  12539. III. If you know the name of the program, but do no know what group it was
  12540. posted in, try using archie and search based on the name. Since most sites
  12541. store the archives by group and volume, the information returned will tell you
  12542. what newsgroup and volume it was posted in. Then you can retrieve the item
  12543. from any archive site for that newsgroup.
  12544. IV. If you do not even know the name, but know you are looking for source code
  12545. that does xxx, retrieve the indexes for each of the newsgroups and see if any
  12546. of the entries (usually listed as the archive name and a short description of
  12547. the function) look reasonable. If so, try those. Or, make a query to archie
  12548. based on some keywords from the function of the software and perhaps it can
  12549. find items that match.
  12550.  
  12551.  
  12552. CD-ROM Archives Available
  12553.  
  12554.  
  12555. Even if the above access methods don't work for you, you can buy CD-ROMs that
  12556. archive sources from USENET and from other services as well. Two of the larger
  12557. publishers are Walnut Creek CD-ROM and Prime Time Freeware.
  12558. Walnut Creek CD-ROM, 1547 Palos Verdes Mall, Suite 260, Walnut Creek, CA (800)
  12559. 786-9907 or (510) 947-5996, publishes several CD-ROMs each year. Some contain
  12560. the Simte120 MS-DOS Archive, others the X and GNU archives, and still others
  12561. MS-Windows sources, and other collections of sources and binaries. Disks run
  12562. from $25 to $60 each (varies by title) plus shipping. In addition, they offer
  12563. those hard-to-find CD-CADDYs at reasonable prices.
  12564. Prime Time Freeware, 370 Altair Way, Suite 150, Sunnyvale, CA 94086, (408)
  12565. 738-4832, <ptf@cfcl.com), publishes twice a year a collection of freely
  12566. distributable source code, including the complete USENET archives. Their disks
  12567. run about $60 each set plus shipping. They also offer a standing subscription
  12568. plan at a discount.
  12569. Now it's time to look at some of the new stuff on the 'net.
  12570.  
  12571.  
  12572. Perl Shines Bright Anew
  12573.  
  12574.  
  12575. Perl 5.000 was finally released after a long beta period. Perl 5.000 is a
  12576. cleaner and more portable version that supports a greatly simplified grammar
  12577. and a tighter, faster interpreter. Perl, the interpreter for the Perl
  12578. Programming Language, is a combination of sed, the shell, and awk. Perl is an
  12579. extremely useful tool for writing scripts and filters. It has been ported to
  12580. UNIX and VMS, and ports of 5.000 to MS-DOS and Windows/NT are in the works.
  12581. Additional features in 5.000 over version 4.036 include the following:
  12582.  
  12583. optional compile-time restrictions on dubious constructs
  12584. greatly improved diagnostics
  12585. both lexical and dynamic scoping
  12586. anonymous data types: scalars, lists, hash table, functions
  12587. arbitrarily nested data structures
  12588. modularity and reusability
  12589. object-oriented programming
  12590. package constructors and destructors
  12591. embeddability and extensibility
  12592. dynamic loading of C libraries
  12593. a POSIX 1003.1 compliant interface
  12594. multiple simultaneous DBM implementations: dbm, sdbm, ndbm, gdbm, and berkeley
  12595. db
  12596. operator overloading on arithmetic types -- supplied: Complex, BigInt, and
  12597. Bigfloat
  12598. regular-expression enhancements
  12599. extensions: curses, X11 support via Sx (Athena, Xlib), and Tk.
  12600. In addition the documentation has been entirely overhauled into both standard
  12601. man interface and HTML versions.
  12602. Perl 5.000 was posted as perl in Volume 45, Issues 64-128 of comp.sources.misc
  12603. and contributed, as usual, by Larry Wall <lwall@netlabs.com>.
  12604.  
  12605.  
  12606. Slip-Like Connections
  12607.  
  12608.  
  12609. Michael O'Reilly wrote and Bill C. Riemers <bcr@physics.purdue.edu> submitted
  12610. term-2.1.2 for Volume 28, Issues 164-172 of comp.sources.unix.term simulates
  12611. many Slip-like features through an ordinary user's account. term requires no
  12612. kernel support on either end, and no root access on either end. It is built to
  12613. run over a modem to connect a non-internet machine with an internet machine.
  12614. If your machine is of the former type, and you don't have slip/ppp, then term
  12615. is for you. If you do have slip/ppp, then you should probably use it instead.
  12616. term is run at both ends, and does multiplexing, error correction, and
  12617. compression across the serial link between them. term is designed to be as
  12618. efficient as possible, with good response time, even over slow modems. term
  12619. behaves the same from both ends. A user on either machine can connect to the
  12620. other.
  12621. term includes several clients, including the following:
  12622. trsh This client runs a shell on the remote end. It's like a normal login
  12623. (similar to "rsh" without the need to specify a hostname).
  12624. tupload The usage is tupload <local file> -as <remote file>.file uploads a
  12625. file, taking the name of the local file and the optional argument as the name
  12626. of the remote file. This format is a bit more robust than that of "rcp," but
  12627. at the price of not allowing specification of a different host or username.
  12628. txconn This client hangs around in the background waiting for X connections,
  12629. and re-directs them to the local X server. txconn is intended to be run on the
  12630. remote machine.
  12631. tredir This client lets you alias a port on one system to another, i.e.,
  12632. 'tredir 4000 23' run on host. 'a' means that anyone telneting to port 4000 on
  12633. 'a' will get a login prompt of machine 'b'.
  12634. tshutdown Terminates term.
  12635.  
  12636.  
  12637. Magic Image Manipulation
  12638.  
  12639.  
  12640. ImageMagick, the rather complete image manipulation package for X, was updated
  12641. by Cristy <cristy@eplrx7.es.duPont.com> for Volume 22, Issues 35-87 of
  12642. comp.sources.x.
  12643. ImageMagick is an X11 package for display and interactive manipulation of
  12644. images. The package includes tools for image conversion, annotation,
  12645. compositing, animation, and creating montages. ImageMagick can read and write
  12646. many of the more popular image formats (e.g. JPEG, TIFF, PNM).
  12647. ImageMagick includes the following:
  12648. display This is a machine-architecture-independent image and display program.
  12649. It can display an image on any workstation display running an X server.
  12650. display can read and write many of the more popular image formats (JPEG, TIFF,
  12651. PNM, etc.). You can perform these functions on the image:
  12652. display information about the image
  12653. write the image to a file
  12654. print the image to a PostScript printer
  12655. delete the image file plus load an image from a file
  12656. display the former image
  12657. select the image to display by its thumbnail rather than name
  12658. undo last image transformation
  12659. restore the image to its original size
  12660. halve the image size
  12661. double the image size
  12662. resize the image
  12663. trim the image edges
  12664. clip the image
  12665. cut the image
  12666. flop image in the horizontal direction
  12667. flip image in the vertical direction
  12668. rotate the image 90 degrees clockwise
  12669. rotate the image 90 degrees counter-clockwise
  12670. rotate the image
  12671. shear the image
  12672.  
  12673. invert the colors of the image
  12674. Perform Histogram Equalization on the image
  12675. Perform histogram normalization on the image
  12676. gamma-correct the image
  12677. reduce the speckles within an image
  12678. detect edges within the image
  12679. convert the image to grayscale
  12680. annotate the image with text
  12681. set the maximum number of unique colors in the image
  12682. add a border to the image
  12683. composite image with another
  12684. edit an image pixel color
  12685. edit the image matte information
  12686. add an image comment
  12687. display information about this program
  12688. discard all images and exit program
  12689. change the level of magnification
  12690. import This program reads an image from any visible window on an X server and
  12691. outputs it as an image file. You can capture a single window, the entire
  12692. screen, or any rectangular portion of the screen. You can use the display (see
  12693. display(1)) utility for redisplay, printing, editing, formatting, archiving,
  12694. image processing, etc. of the captured image.
  12695. You can specify the target window by ID or name, or select by clicking the
  12696. mouse in the desired window. If you press a button and then drag, a rectangle
  12697. will form which expands and contracts as the mouse moves. To save the portion
  12698. of the screen defined by the rectangle, just release the button. The keyboard
  12699. bell rings once at the beginning of the screen capture and twice when it
  12700. completes.
  12701. animate This program displays a sequence of images on any workstation display
  12702. running an X server. animate first determines the hardware capabilities of the
  12703. workstation. If the number of unique colors in an image is less than or equal
  12704. to the number the workstation can support, animate displays the image in an X
  12705. window. Otherwise, animate reduces the number of colors in the image to match
  12706. the color resolution of the workstation before display.
  12707. animate's color reduction capability allows a continuous-tone 24 bits/pixel
  12708. image to be displayed on a 8-bit pseudo-color or monochrome device. In most
  12709. instances, the reduced color image closely resembles the original.
  12710. Alternatively, a monochrome or pseudo-color image sequence can display on a
  12711. continuous-tone 24 bits/pixels device.
  12712. montage This program creates a composite image by combining several separate
  12713. images. The images appear tiled on the composite image with the name of each
  12714. image optionally appearing just below the individual tile.
  12715. mogrify This program applies transforms to an image or a sequence of images.
  12716. Possible transforms include image scaling, image rotation, color reduction,
  12717. and others. The transmogrified image overwrites the original image.
  12718. convert This program converts an input file using one image format to an
  12719. output file with a differing image format. By default, convert determines the
  12720. image format by its magic number. To specify a particular image format,
  12721. precede the filename with an image format name and a colon (e.g. ps:image) or
  12722. specify the image type as the filename suffix (e.g. image.ps). Specify file as
  12723. - for standard input or output. If file has the extension .Z, the file is
  12724. decoded with uncompress.
  12725. combine This program combines images to create new images.
  12726. segment This program segments an image by analyzing the histograms of the
  12727. color components and identifying units that are homogeneous with the fuzzy
  12728. c-means technique. The scale-space filter analyzes the histograms of the three
  12729. color components of the image and identifies a set of classes. segment uses
  12730. the extents of each class to coarsely segment the image with thresholding. It
  12731. determines the color associated with each class by the mean color of all
  12732. pixels within the extents of a particular class. Finally, segment assigns any
  12733. unclassified pixels to the closest class with the fuzzy c-means technique.
  12734. xtp This is a utility for retrieving, listing, or printing files from a remote
  12735. network site, or sending files to a remote network site. xtp performs most of
  12736. the same functions as the ftp program, but does not require any interactive
  12737. commands. You simply specify the file transfer task on the command line and
  12738. xtp performs the task automatically.
  12739.  
  12740.  
  12741.  
  12742.  
  12743.  
  12744.  
  12745.  
  12746.  
  12747.  
  12748.  
  12749.  
  12750.  
  12751.  
  12752.  
  12753.  
  12754.  
  12755.  
  12756.  
  12757.  
  12758.  
  12759.  
  12760.  
  12761.  
  12762.  
  12763.  
  12764.  
  12765.  
  12766.  
  12767.  
  12768.  
  12769.  
  12770.  
  12771.  
  12772.  
  12773.  
  12774.  
  12775. Editor's Forum
  12776. I have to say that I feel a little sorry for Intel. It takes a lot of
  12777. microcode to do what a Pentium does. To expect every byte of that code to be
  12778. correct is asking a lot. Those of us who work on intimate terms with
  12779. microprocessors -- particularly the first batch or two of a new breed -- know
  12780. that microcode bugs are actually rather common. I can't count the number of
  12781. kludges I've added to code generators, and to runtime support functions, to
  12782. sidestep bad instructions in one or more members of a family of CPUs.
  12783. The effects of buggy microcode are usually not quite so pernicious as the one
  12784. that haunts a couple million Pentia. If a program hangs, or goes barmy, you
  12785. tend not to respect the numbers it has just generated. You are also motivated
  12786. to change the program until it avoids acting demonstrably nutty.
  12787. But when the numbers look fine, or maybe just a little suspicious, you're more
  12788. inclined to doubt your intuition. After all, the whole idea of computers is to
  12789. be very fast and very accurate in doing rather stupid things. We neither
  12790. expect nor want creativity in floating-point arithmetic.
  12791. I've been writing and testing math functions for several decades now. While I
  12792. keep getting better at it, I still confess to shipping more than a few buggy
  12793. lines of code. It's horribly easy to lose bits of precision, particularly for
  12794. extreme argument values, and not catch the problem even with a fairly
  12795. responsible test discipline. When you're piecing together a multipart result
  12796. -- as the Pentium evidently does on a floating-point divide -- it's easy to
  12797. lose a bit or two out of the middle as well.
  12798. Where I fault Intel is in their public relations. You don't hide such a
  12799. serious flaw from your customers. When it becomes widely known, you don't pooh
  12800. pooh legitimate expressions of concern. And when you fix the product, you
  12801. don't make customers jump through hoops to prove they deserve a properly
  12802. functioning replacement. At least Intel eventually had the sense to wise up
  12803. and apologize.
  12804. I have observed more than once that software is seen more as a subscription
  12805. service than a one-time sale. I have also predicted that the increasing use of
  12806. microcode, ROMs, and EPROMs will shift the perception of more and more
  12807. hardware in the same direction. Intel, to their credit, has created a mass
  12808. market that expects regular and dramatic improvements in processor technology.
  12809. It's too bad for them that they were slow to recognize the change in public
  12810. perception that comes with this success, and to act accordingly.
  12811. Those of us who deliver products based on C and C++ should view this episode
  12812. as a cautionary tale. We need to check our work as thoroughly as budgets and
  12813. schedules allow. Then we need to stand ready to fix the bugs that inevitably
  12814. go out the door. For many of us, writing software is a happy end in itself,
  12815. but to our customers the software is just a means to a different end.
  12816. P.J. Plauger
  12817. pjp@plauger.com
  12818.  
  12819.  
  12820.  
  12821.  
  12822.  
  12823.  
  12824.  
  12825.  
  12826.  
  12827.  
  12828.  
  12829.  
  12830.  
  12831.  
  12832.  
  12833.  
  12834.  
  12835.  
  12836.  
  12837.  
  12838.  
  12839.  
  12840.  
  12841.  
  12842.  
  12843.  
  12844.  
  12845.  
  12846.  
  12847.  
  12848.  
  12849.  
  12850.  
  12851.  
  12852.  
  12853.  
  12854.  
  12855.  
  12856.  
  12857.  
  12858.  
  12859.  
  12860.  
  12861.  
  12862.  
  12863.  
  12864.  
  12865.  
  12866.  
  12867.  
  12868.  
  12869. New Products
  12870.  
  12871.  
  12872. Industry-Related News & Announcements
  12873.  
  12874.  
  12875.  
  12876.  
  12877. Rogue Wave Introduces DBtools.h++
  12878.  
  12879.  
  12880. Rogue Wave Software has introduced DBtools.h++, a library of foundation
  12881. classes to support C++ programmers in creating SQL database applications.
  12882. DBtools.h++ provides C++ components for object-oriented development on the
  12883. client side, while retaining the benefits of their installed RDBMS.
  12884. DBtools.h++ provides developers with a portable, cross-platform interface to
  12885. SQL databases. This interface bridges the relational and object-oriented
  12886. models by encapsulating fundamental relational constructs such as tables,
  12887. rows, and cursors in C++ classes. Other features of DBtools.h++ include
  12888. facilities for manipulating dates, times, strings, and in-memory data
  12889. structures.
  12890. DBtools.h++ consists of 36 public classes for encapsulating relation
  12891. constructs and data types, plus 100 data structures and utility classes found
  12892. in Tools.h++, which is integrated into DBtools.h++. DBtools.h++ also binds the
  12893. basic data types available in relational database systems to its own
  12894. higher-level C++ data structures. DBtools.h++ achieves portability across
  12895. databases through a two-tiered architecture that includes a public interface
  12896. and separate Access Libraries for the various supported databases. Because
  12897. DBtools.h++ supports shared libraries, UNIX developers can switch between
  12898. databases without relinking, just as Windows developers do with DLLs.
  12899. DBtools.h++ supports ORACLE7, Sybase SQL Server, Microsoft SQL, and ODBC
  12900. (Windows only). Prices for DBtools.h++ start at $395 for Windows and $495 for
  12901. UNIX. For more information contact Rogue Wave Software, Inc., 260 S.W.
  12902. Madison, P.O. Box 2328, Corvallis, OR 97339; (800) 487-3217 or (503) 754-3010;
  12903. FAX: (503) 757-6650.
  12904.  
  12905.  
  12906. ObjectSpace Announces Product Line
  12907.  
  12908.  
  12909. ObjectSpace, Inc. has announced an object-oriented product line that supports
  12910. C++ and Smalltalk. The products are ObjectSystems, ObjectSockets,
  12911. ObjectMetrics, and ObjectCatalog. The products give developers a variety of
  12912. solutions depending on their need: ObjectSystems is for C++ developers using
  12913. UNIX, ObjectSockets is for Smalltalk developers using TCP/IP communications,
  12914. ObjectMetrics gathers object-oriented metrics for Smalltalk developers, and
  12915. ObjectCatalog is a corporate reuse facility.
  12916. ObjectSystems is a C++ framework for cross-platform UNIX systems and
  12917. development. Developers can either use the entire ObjectSystem library as a
  12918. framework for UNIX development or in conjunction with Rogue Wave Tools.h++.
  12919. Features of Objectsystems include support of IPC mechanisms, including pipes,
  12920. sockets, message queues, shared memory, and semaphores; sending objects over a
  12921. UNIX IPX mechanism or storing them on disk; and an eventhandling subsystem
  12922. that integrates with C++ exception handling.
  12923. ObjectSockets is a class library with 40 classes representing the aspects of
  12924. TCP/IP communications, including TCP sockets, UDP sockets, IP addresses,
  12925. hosts, network protocols, and services. ObjectSockets comes with source code
  12926. and a user guide. Operated via a graphical interface, Objectmetrics gathers
  12927. metrics that can indicate problem areas such as overly complex code,
  12928. unnecessary coupling between modules, and programmer productivity. Features of
  12929. the reuse facility, ObjectCatalog, include automatic publication of C++ and
  12930. Smalltalk classes; publish and search capabilities for descriptions of
  12931. classes, frameworks, and documents; user-defined classifications; searching of
  12932. local and distributed catalogs; and the capability to register interest in a
  12933. component.
  12934. ObjectSystem is priced at $875 per user. ObjectSockets is priced at $695 per
  12935. user, and ObjectMetrics is priced at $595 per user. Site licensing is
  12936. available. For more information contact ObjectSpace, Inc., 14881 Quorum Dr.,
  12937. Suite 400, Dallas, TX 75240; (214) 934-2496; FAX: (214) 663-3959.
  12938.  
  12939.  
  12940. MainSoft Upgrades MainWin
  12941.  
  12942.  
  12943. MainSoft Corporation has upgraded MainWin, its "Windows API to UNIX" cross
  12944. development technology. The MainWin technology is an implementation of the
  12945. Windows API directly on UNIX. The MainWin library, the equivalent of the
  12946. Windows DDL on a PC, executes in a high-performance, native RISC code. A
  12947. ported application's C/C++ code is also compiled into native RISC code. Users
  12948. of MainWin-ported applications can specify whether the applications have the
  12949. look of Windows or Motif.
  12950. Mainsoft has access to the Windows source code through a licensing agreement
  12951. with Microsoft, facilitating the development of MainWin. Features of MainWin
  12952. 1.1 include a Dynamic Data Exchange Management Library (DDEML), and support
  12953. for 16-bit Windows 3.1 and 32-bit Windows NT code. Another feature of MainWin
  12954. 1.1 is that both 16- and 32-bit support is provided by a single library.
  12955. MainWin 1.1, a shared library on UNIX workstations, can support applications
  12956. compiled from Windows 3.1 and Windows NT source code executing simultaneously.
  12957. In addition, MainWin 1.1 adds support for many of Win32-specific APIs. MainWin
  12958. 1.1 also includes increased window and repaint speeds. According to MainSoft,
  12959. display speeds for complex dialog boxes have more than doubled.
  12960. MainWin 1.1 supports Sun SPARC with Solaris 2.2, Sun SPARC with SunOS 4.1, HP
  12961. 9000/7000 and 8000 with HP-UX 9.0, IBM RS/6000 with AIX 3.2, and SGI Iris or
  12962. Indigo with IRIX 5.1. For more information contact MainSoft Corporation, (408)
  12963. 774-3405.
  12964.  
  12965.  
  12966. ACE Releases ACE EXPERT
  12967.  
  12968.  
  12969. ACE Associated Computer Experts has released the ACE EXPERT SPARC Compilers.
  12970. The ACE EXPERT SPARC Compilers combine EXPERT front ends for ANSI C, K&R C,
  12971. FORTRAN 77, Pascal, and Modula 2 with a computer generated SPARC code
  12972. generator, which has been generated from a concise architecture description.
  12973. According to the company, the ACE EXPERT SPARC Compilers have undergone
  12974. extensive field testing, and the robustness resulting from the use of
  12975. generators was proven using large industrial programs. A feature of the ACE
  12976. EXPERT SPARC Compilers is that program modules written in different languages
  12977. can be mixed by linking them together.
  12978. The ACE EXPERT SPARC Compilers are available on CD-ROM for Solaris 2. The ACE
  12979. EXPERT SPARC Compilers interface to the Solaris linkage editor and to
  12980. debuggers such as TotalView and dbx. The CD-ROM containing the five ACE EXPERT
  12981. SPARC Compilers and online documentation is priced at $499. For more
  12982. information contact ACE Associated Computer Experts, Inc., Van Eeghenstraat
  12983. 100, 1071 GL Amsterdam, The Netherlands; +31 20 6646416; FAX: +31 20 6750389;
  12984. Telex: 11702 (ace pl).
  12985.  
  12986.  
  12987. StratosWare Ships MemCheck v3.0 for Windows
  12988.  
  12989.  
  12990. StratosWare has begun shipping MemCheck v3.0 for Windows, an automatic
  12991. error-detection tool for C/C++ programmers. With the inclusion of a single
  12992. header file, MemCheck for Windows tracks an application's GDI objects
  12993. including device contexts, pens, bitmaps, brushes, fonts, metafiles, and
  12994. regions. GDI objects that are not released are identified by file and line.
  12995. MemCheck will notify users of invalid operations as well as of the use of
  12996. invalid Windows or object handles. MemCheck also tracks resources loaded or
  12997. created by an application. In addition, MemCheck v3.0 for Windows detects
  12998. memory overwrites and underwrites, memory leaks, invalid handle/pointer usage,
  12999. out-of-memory conditions, and other memory errors in GlobalAlloc(),
  13000. LocalAlloc(), malloc(), free(), and allocation/reallocation functions. Data
  13001. transfer functions are also intercepted by MemCheck to check for overwrites to
  13002. heap and other variables.
  13003. MemCheck v3.0 integrates with existing C/C++ code and works at run time to
  13004. identify errors by source file and line number in the source code. Developers
  13005. can use MemCheck in combination with other debuggers such as CodeView or
  13006. TurboDebugger. Developers may ship applications with MemCheck linked in,
  13007. royalty-free, allowing detection of errors at customer or test sites.
  13008. MemCheck v3.0 supports Microsoft C/C++ 7.0, Visual C/C++, and Borland C++
  13009. 2.0-4.x. MemCheck v3.0 for Windows is priced at $139 and the upgrade price
  13010. from v2.1 is $59. For more information contact StratosWare Corporation, 1756
  13011. Plymouth Road, Suite 1500, Ann Arbor, MI 48105-1890; (800) 933-3284; BBS:
  13012. (313) 996-2993.
  13013.  
  13014.  
  13015. Reasoning Systems Releases Software Refinery 4.0
  13016.  
  13017.  
  13018. Reasoning Systems has released Software Refinery 4.0, which builds software
  13019. source code analysis and conversion tools on UNIX workstations. Tools that can
  13020. be developed by Software Refinery include reverse-engineering tools that help
  13021. users understand and modify unfamiliar legacy systems; conversion tools that
  13022. port source code to other programming languages, operating systems, and
  13023. databases; and quality assurance tools that point out bugs and unmaintainable
  13024. code.
  13025. Software Refinery includes a parser generator, which lets tools work with the
  13026. source code. It also includes an object base, which stores a representation of
  13027. source code and associated information between sessions and allows sharing of
  13028. data on a LAN. Software Refinery's unit-level incremental compilation and
  13029. dynamic linking provide a short edit-compile-debug loop. A GUI toolkit,
  13030. integrated in Software Refinery, lets users develop graphical interfaces using
  13031. windows, menus, and dialog boxes. Also included in Software Refinery is a
  13032. high-level language, which lets users develop programs to analyze and modify
  13033. source code using objects, logic programming, pattern-matching, and
  13034. transformation rules.
  13035. Features of Software Refinery 4.0 include Workbench, a library of reusable
  13036. reverse engineering components including structure charts, flow graphs, and
  13037. coding standards; a "surface syntax" facility which overlays the
  13038. object-oriented tree representation of source code with the original program
  13039. text; windows types (outline, tables, and directory browsers); and a graphical
  13040. data inspector.
  13041. Software Refinery 4.0 supports UNIX workstations on Sun, IBM, and HP
  13042. platforms. For more information contact Reasoning Systems, 3260 Hillview Ave.,
  13043. Palo Alto, CA, 94304; (415) 494-6201; FAX: (415) 494-8053.
  13044.  
  13045.  
  13046.  
  13047. AJS Publishing Introduces VBXpress
  13048.  
  13049.  
  13050. AJS Publishing, Inc. has introduced VBXpress Custom Control Design System.
  13051. With VBXpress, a user can design original screen objects for Windows, such as
  13052. custom sliders, tool bars, floating tool palettes, flyouts, rocker buttons,
  13053. spin buttons, and graphic command buttons. VBXpress then turns the screen
  13054. object control into a VBX custom control without programming. The resulting
  13055. VBX controls can be used to add user-interface features to programs written in
  13056. Visual C++, Visual Basic, Borland C++ 4.0, dBASE for Windows, or other
  13057. VBX-conforming languages or application development systems.
  13058. VBXpress creates VBX controls based on the definitions specified by the user
  13059. through the Control Editor. The VBXpress Control Editor is a menu-driven,
  13060. point-and-click program, which assembles image, color, text, behavior, and
  13061. style selections into a screen object. Images can be applied from four
  13062. available palettes or created new and imported from an external bitmap editor.
  13063. The control can then be customized from a variety of available events and
  13064. properties. When a new control's definition is finished, it is stored in a
  13065. VBXpress resource file with a VBM extension. VBM files can be reopened later
  13066. for modifications and additions. VBXpress will automatically generate the new
  13067. VBX file containing the functionality of the newly-defined control. According
  13068. to AJS, the resulting file is a fully-compatible, Level One VBX control.
  13069. The VBXpress Custom Control Design System includes the Control Editor program,
  13070. VBXpress Conductor wizard, documentation, and help system. The VBXpress Custom
  13071. Control Design System is priced at $299. There are no royalties or runtime
  13072. fees. For more information contact AJS Publishing, Inc., P.O. Box 83220, Los
  13073. Angeles, CA 90083; (800) 992-3383 or (310) 215-9145; FAX: (310) 215-9135
  13074.  
  13075.  
  13076. Software Blacksmiths Ships C-DOC 6.0
  13077.  
  13078.  
  13079. Software Blacksmiths has begun shipping C-DOC 6.0, an upgrade of their
  13080. automatic program documentation tool for Windows 3.1, Windows NT, OS/2, and
  13081. DOS. C-DOC generates information that is used to modify and maintain C/C++
  13082. software products. C-DOC includes six modules: C-CALL, C-CMT, C-LIST, C-REF,
  13083. C-METRIC, and C-BROWSE.
  13084. Features of C-DOC 6.0 include a native Windows NT/Win 32 and Windows/Win16
  13085. version, support for long Win32 and OS/2 file names, integrated graphical tree
  13086. and text viewing, and caller/called and called/caller function hierarchy
  13087. trees. Other features of C-DOC 6.0 include a RUN option in help; RTF for input
  13088. in word processors like Microsoft Word, Lotus AMI, or WordPerfect; and
  13089. 1,000,000 line capacity for Win32, OS/2, and extended DOS.
  13090. C-DOC 6.0 supports Windows 3.1, NT, OS/2, or DOS-compatible PCs. C-DOC 6.0
  13091. standard edition for DOS, supporting up to 10,000 source lines in multiple
  13092. files and directories, is priced at $199. C-DOC Professional, supporting the
  13093. mentioned environments and programs of up to 1,000,000 lines in multiple files
  13094. and directories, is priced at $299. Individual C-DOC modules are priced at $69
  13095. each. For more information contact Software Blacksmiths, Inc., 6064 St. Ives
  13096. Way, Mississauga, Ontario, Canada L5N 4M1; (905) 858-4466; FAX: (905)
  13097. 858-4466; BBS: (905) 858-1916; E-mail: swbksmth@flexnet.com.
  13098.  
  13099.  
  13100. Microware Announces FasTrak
  13101.  
  13102.  
  13103. Microware Systems Corporation has announced FasTrak real-time,
  13104. cross-development tools for Windows. Supporting Windows 3.1 on 386/486 and
  13105. Pentium systems, FasTrak creates C applications for Intel 80x86, Motorola
  13106. 680x0, and PowerPC systems that run Microware's OS-9 RealTime Operating
  13107. System. Communications between the Windows host and OS-9 target is conducted
  13108. via Ethernet TCP/IP or serial link.
  13109. FasTrak integrates tools that automate the creation, debugging, analysis, and
  13110. management of complex real-time software development projects. Bundled with
  13111. Microware's Ultra C optimizing C compiler, FasTrak includes tools for code
  13112. creation and revision, code generation, source-level debugging, system and
  13113. application profiling, and software version control. FasTrak for Windows
  13114. contains three core modules: a MakeFile Editor, which contains a pushbutton
  13115. utility for generating makefiles; the FastFix Debugger, a C source level
  13116. debugger, which resides on the host; and the FasTrak Target Monitor, which
  13117. adds system-level debug and application profiling capabilities.
  13118. FasTrak for Windows is priced at $3,750. For more information contact
  13119. Microware Systems Corporation, 1900 N.W. 114th St., Des Moines, IA 50325-7077;
  13120. (800) 9000 or (515) 224-1929; FAX: (515) 224-1352; Internet:
  13121. info@microware.com.
  13122.  
  13123.  
  13124. SciTech Publishes Software For Science Volume 26
  13125.  
  13126.  
  13127. SciTech International, Inc., has published Volume 26 of Software For Science,
  13128. a comprehensive catalog of scientific and technical software for DOS, Windows,
  13129. Macintosh, and UNIX workstations. Volume 26 lists 1,750 products, including
  13130. 250 new products, as well as articles that contain information about
  13131. scientific and technical problems, tips, and "how to" information on buying
  13132. complex scientific and technical software. SciTech's product database contains
  13133. information on 3,000 scientific and technical tools.
  13134. Software for Science Volume 26 is free of charge. Electronic versions of
  13135. Software for Science are available for Windows and the Internet. For more
  13136. information contact SciTech International, Inc., Bailiwick Court Bldg., 2231
  13137. N. Clybourn Ave., Chicago, IL 60614-3011; (800) 622-3345 or (312) 472-0444;
  13138. FAX: (312) 472-0472; E-mail: info@scitechint.com.
  13139.  
  13140.  
  13141. SSI Upgrades Link&Locate 386 and Announces Tools for Embedded Processors
  13142.  
  13143.  
  13144. Systems & Software, Inc. (SSI) has upgraded Link&Locate 386, their
  13145. Linker/Locator/Builder for embedded x86 software development using C/C++.
  13146. Features of Link&Locate 386 v2.0 include 32-bit COFF support, run-time
  13147. support, DOS and Windows NT executables, and control of segment ordering.
  13148. Link&Locate 386 v2.0 supports the following compilers and assemblers:
  13149. Microsoft Visual C++ 1.0 for NT, MetaWare High C/C++ for NT, WATCOM C/C++/32
  13150. 10.0, Microsoft MASM 6.11, and Phar Lap 386ASM 6.x. Native versions of
  13151. Link&Locate are offered for DOS and NT. The Link&Locate 386 package includes
  13152. EPROM programmer utilities, a librarian, and is interoperable with SSI's
  13153. family of 32-bit development tools and debuggers.
  13154. In another announcement, SSI has begun offering a series of tools to support
  13155. application development based on AMD's AM486SE, AM386EM, and AM186EM embedded
  13156. processors. SSI offers three lines of development tools that are compatible
  13157. with the AM386 family: SP/Tools, CV/Tools, and OMF/Tools. SP/Tools is a family
  13158. of tools designed to extend MetaWare's High C/C++ 32-bit compiler and
  13159. Microsoft's MASM assembler for embedded development with the AM386/486 family.
  13160. CV/Tools would be used in conjunction with Microsoft's Professional C/C++ and
  13161. MASM for 16-bit real-mode embedded applications. OMF/Tools support the
  13162. OMF86/286/386 file formats for development of real- or protected-mode embedded
  13163. C applications.
  13164. For more information contact Systems & Software, Inc., 18012 Cowan, Suite 100,
  13165. Irvine, CA 92714-6809; (714) 833-1700; FAX: (714) 833-1900.
  13166.  
  13167.  
  13168. Cavendish Releases NewTrack V3
  13169.  
  13170.  
  13171. Cavendish Software Ltd. has released NewTrack v3 memory allocation tracker for
  13172. C/C++ under Windows. NewTrack v3 supports Win 16 and Win32 development, using
  13173. Borland C++ and Microsoft Visual C++.
  13174. NewTrack v3 is offered as shareware. NewTrack v3 can be registered for $99 per
  13175. user. NewTrack v3 site licenses start at $1000. NewTrack v3 can be downloaded
  13176. from CompuServe. The Microsoft Visual C++ version can be found in the MSLANG
  13177. forum, and the Borland C++ version in the BCPPWIN. Use the keyword NewTrack to
  13178. locate the files. For more information contact Cavendish Software Ltd., 50
  13179. Avenue Rd., Trowbridge, Wilts BA14 OAQ, England; (44) 1225-763598; FAX: (44)
  13180. 1225-777359; Email: market@cavesoft.demon.co.uk.
  13181.  
  13182.  
  13183. AIB Announces Sentinel II
  13184.  
  13185.  
  13186. AIB Software Corporation has announced Sentinel II, a multi-platform, runtime
  13187. memory testing tool for UNIX C and C++. Sentinel II, inserted into the build
  13188. and test processes, detects memory access errors and memory leaks as they
  13189. occur in the code. Sentinel II uses OMT technology, which lets Sentinel
  13190. monitor the memory accesses performed by an application and detect memory
  13191. errors. OMT is a process by which Sentinel converts object code into a
  13192. system-independent representation of the program under test. Sentinel II then
  13193. transfers the representation into machine-dependent object code with debugging
  13194. capabilities.
  13195. Sentinel II will support Sun SPARC, HP PA-RISC, and IBM RS6000 platforms. For
  13196. more information contact AIB Software Corporation, 46030 Manekin Plaza,
  13197. Dulles, VA 20166; (703) 430-9247; FAX: (703) 450-4560; Internet: info@aib.com.
  13198.  
  13199.  
  13200. Black Ice Announces FAX C++ SDK
  13201.  
  13202.  
  13203. Black Ice Software, Inc. has announced FAX C++ SDK. The FAX C++ Toolkit
  13204. include standard device drivers for Windows to support Class 1, Class 2, and
  13205. Class 2.0 fax modems. The FAX C++ is object oriented and provides both a C++
  13206. interface and C User interface. FAX C++ handles a variety of image formats
  13207. including TIFF/CCITT G3, PCX, DCX, and compressed and uncompressed Microsoft
  13208. Device Independent Bitmaps (DIB). Other features of FAX C++ include support
  13209. for both standard American and European paper sizes, send and receive queue
  13210. management, dynamic configuration of faxing device, communication port
  13211. control, and selection of page orientations. FAX C++ consists of a set of
  13212. class definitions with 70 member functions which manage communicationn ports
  13213. and fax modems. Additionally, FAX C++ supports data flow to and from classes
  13214. such as TCommClassOne and TCommClassTwo.
  13215. FAX C++ supports Microsoft Visual C++, Visual Basic, SQL Windows, Borland C++,
  13216. and environments compatible with DLLs. FAX C++ is priced at $2,000 and is
  13217. royalty free. For more information contact Black Ice Software, Inc., 113 Route
  13218. 112, Amherst, NH 03031; (603) 673-1019; FAX: (603) 672-4112.
  13219.  
  13220.  
  13221.  
  13222. Dart Communications Introduces PowerTCP
  13223.  
  13224.  
  13225. Dart Communications has introduced PowerTCP, a set of protocol libraries that
  13226. provide turn-key TCP/IP protocols for a flat license fee. PowerTCP is a second
  13227. generation product based on the GCP++ line of tools, which provide 16-bit
  13228. support for building TCP, UDP, TELNET, TFTP, and VT-220 applications over the
  13229. Windows Sockets API. Features of the PowerTCP libraries include 32-bit
  13230. support, FTP support, a C++ API, a new internal architecture, and expanded OEM
  13231. licensing. PowerTCP provides both DLL (16- and 32-bit) and VBX interfaces.
  13232. For more information contact Dart Communications, 6 Occum Ridge Rd.,
  13233. Deansboro, NY 13328-1008; (315) 841-8106; FAX: (315) 841-8107.
  13234.  
  13235.  
  13236. Menai Ships the Gamelon File I/O Library
  13237.  
  13238.  
  13239. Menai Corporation has begun shipping Gamelon File I/O Library, an
  13240. object-oriented programming tool for OS/2 and Windows/NT. Gamelon, written in
  13241. C++ and built around the concept of object storage, lets developers build
  13242. flexible file structures using combinations of aggregate and data objects.
  13243. Features of Gamelon include aggregate object nesting, logical navigation, and
  13244. automatic object tracking. The API provides free-form data storage and data
  13245. association capabilities.
  13246. In addition to the file I/O library and API for one programming language,
  13247. Gamelon is packaged with several support tools, including the Browser, the
  13248. Text Compiler, and the File Decompiler. Gamelon File I/O Library is priced at
  13249. $395 for the Windows v3.x and $495 for the OS/2 and Windows NT versions. For
  13250. more information contact Menai Corporation, 1010 El Camino Real, Suite 370,
  13251. Menlo Park, CA 94025-4335; (800) 426-3566 or (415) 617-5730; FAX: (415)
  13252. 853-6453; BBS: (415) 617-5726.
  13253.  
  13254.  
  13255. Tower Technology Releases TowerEiffel/O2
  13256.  
  13257.  
  13258. Tower Technology Corporation and O2 Technology, Inc. have released the
  13259. TowerEiffel/O2 Object-Oriented Data Base Interface. Tower and O2 have
  13260. integrated their products to allow TowerEiffel programs to use the features
  13261. and capabilities of the O2 ODBMS, including persistence, collection, and
  13262. referential integrity. The O2 object data model and Eiffel object notation
  13263. include identity, class, types, encapsulations, multiple inheritance,
  13264. redefinition, and late binding. The Tower Eiffel/O2 interface consists of a
  13265. set of Eiffel classes and tools that allow persistent storage of Eiffel
  13266. objects.
  13267. The O2 ODBMS, including the TowerEiffel/O2 interface components, supports
  13268. SunOS, Solaris, HPUX, and NEXTSTEP. O2 ODBMS is priced at $3,995 for a
  13269. single-user license. For more information contact Tower Technology
  13270. Corporation, Portland, OR; (512) 452-9455; E-mail: tower@twr.com.
  13271.  
  13272.  
  13273. Advanced Computing Labs Introduces Neural++
  13274.  
  13275.  
  13276. Advanced Computing Labs has introduced Neural++, a collection of neural
  13277. networks packages, as a C++ library. Neural++ contains the code for the
  13278. following neural networks: BP, CPN, Kohonen, and Outstar. Within a DOS/Windows
  13279. C++ framework, Neural++ automates data scaling and Z-score data
  13280. pre-processing. Features of Neural++ include C++ class programming references,
  13281. math capabilities, and object persistence via iostreams.
  13282. Neural++ also includes Math++, a set of numerical classes that provide access
  13283. to matrices, vectors, linear algebra, random numbers, regression, simulation,
  13284. and data analysis. Neural++ is priced at $269. For more information contact
  13285. Advanced Computing Labs, P.O. Box 1547, West Chester, OH 45071; (513)
  13286. 779-2716; FAX: (513) 779-4010.
  13287.  
  13288.  
  13289. Sigma Software Ships OOPlot For C/C++
  13290.  
  13291.  
  13292. Sigma Software has begun shipping OOPlot for C/C++, a C/C++ Windows version of
  13293. OOPlot graphing software for business, science, and engineering. OOPlot for
  13294. Windows includes manual and automatic scaling, labeling, overlapping windows,
  13295. major and minor tick marks, colors, grids, and borders; linear, semi-log, and
  13296. log-log axes; and line, scatter, bar, and 3-D pie graphs with real
  13297. coordinates.
  13298. OOPlot for Windows supports Borland C/C++ compilers and includes examples,
  13299. sample programs, and interactive support. OOPlot for Windows is priced at $149
  13300. and is royalty free. For more information contact Sigma Software, 15779
  13301. Columbia Pike, Suite 360, Burtonsville, MD 20866; (301) 549-3320 FAX: (301)
  13302. 549-3320.
  13303.  
  13304.  
  13305. Blue Sky Upgrades RoboHELP
  13306.  
  13307.  
  13308. Blue Sky's RoboHELP v3.0 is a Help Authoring tool for Windows and Windows NT,
  13309. which supports both the U.S. and European versions of Microsoft Word 6.0.
  13310. RoboHELP 3.0 is further integrated with Word 6.0, allowing users to use
  13311. additional Word 6.0 features, such as smart quotes, emdashes, en-dashes,
  13312. bulleted lists, numbered lists, and hanging indents.
  13313. For more information contact Blue Sky Software Corporation, 7486 La Jolla
  13314. Blvd., Suite 3, La Jolla, CA 92037; (800) 677-4946 or (619) 459-6365; FAX:
  13315. (619) 459-6366; CompuServe: 71052,1641.
  13316.  
  13317.  
  13318.  
  13319.  
  13320.  
  13321.  
  13322.  
  13323.  
  13324.  
  13325.  
  13326.  
  13327.  
  13328.  
  13329.  
  13330.  
  13331.  
  13332.  
  13333.  
  13334.  
  13335.  
  13336.  
  13337.  
  13338. We Have Mail
  13339. Dear Mr. Plauger,
  13340. I've been a C Programmer for 10 years now and have regrettably moved on to
  13341. C++. The largest problem I've been having with C++ is the complexity of the
  13342. language; the damn compiler does too much for you. In some cases it overrides
  13343. what you've intended it to do. So I'm in full agreement that a new C proposal
  13344. should keep with the long-standing spirit of C:
  13345. the programmer must be trusted
  13346. the compiler must not make decisions for the programmer
  13347. the language must be small and simple
  13348. With these concepts in mind, I propose the following additions to Mr. Jervis
  13349. proposal:
  13350. a) Constants expression are evaluated at compile time. This is the method used
  13351. by C++ to evaluate constants and should be used by C. This feature is to get
  13352. around this C problem:
  13353. const int CHARBIT * 8;
  13354. const int MAX_BUFSZ = 10 * CHARBIT;
  13355. char *bufarr_ptr[MAX_BUFSZ];
  13356. b) The article didn't mention inline functions. This feature should work for
  13357. both function declarations and header declarations. Yes, we know it would make
  13358. some work for the linker guys. But we could help by doing the following:
  13359. #ifdef _NDEBUG
  13360. # define INLINE inline
  13361. #else
  13362. # #define INLINE /* empty */
  13363. #endif
  13364. INLINE BOOL
  13365. checkOverflow(const char * buf_ptr )
  13366. {
  13367. if (buf_ptr > bufarr_ptr) {
  13368. return TRUE;
  13369. }
  13370. return FALSE;
  13371. }
  13372. We can debug this code by simply flipping compilation flags on/off.
  13373. c) a thread-safe and reduced-error-checking standard C library. The same
  13374. functionality would be used, but all the subroutines would be used as follows:
  13375. typedef struct errorblk {
  13376. char desc[96];
  13377. int errno;
  13378. char name[16]
  13379. } errorblk_t;
  13380. ...
  13381. fh = ts_fopen(&errblk, filenm, "w+");
  13382. The nice thing about using an errorblk_t is that you can call several C
  13383. library calls without checking the return code:
  13384. fh = ts_fopen( &errblk, filenm, "w+");
  13385. ts_fwrite( &errorblk, fh, ....
  13386. ts_lseek( &errorblk, fh, ...
  13387. if ( errorblk.errno != TS_OKAY ) {
  13388. ts_perror( &errorblk );
  13389. }
  13390. This also removes the use of errno and explicitly notifies the library creator
  13391. that all ts_???? routines must be reentrant.
  13392. d) Mr. Jervis did mention inheritance and virtual functions, which I feel are
  13393. key features missing from C. Unfortunately, the mechanism to define the class
  13394. access hierachy is missing from the proposal. The user cannot use the
  13395. protected, public, and private keywords that C++ contains when declaring a
  13396. derived class. An example would be:
  13397. class base { .... }
  13398. class derived /* protected */ base { ... }
  13399. // class derived :: protected base { ... }
  13400. I don't agree with Mr. Jervis's use of the derived keyword because it's
  13401. already a standard, even though his logic is correct.
  13402. e) The last thing I've alway wished C had was a method to reduce structure
  13403. indirections. This is a feature that Pascal has with the with keyword.
  13404. Frequently, I'm forced to create temporaries to track very long structure
  13405. indirections. I've seen other programmers use #defines (really).
  13406. Respectfully,
  13407. David Dennerline
  13408. d.dennerline@bull.com
  13409. Dear Mr. Plauger,
  13410. I have just read Bob Jervis' "All is Flux" article in the October 1994 issue
  13411. of CUJ. I like the ideas he presents and am in total agreement with what he
  13412. says. He has, however, left one issue out which I would like to comment on.
  13413. That issue is how base class virtual methods are called from child classes.
  13414. One of the powerful aspects of virtual methods is that they can be used to
  13415. slightly modify the functionality of the base class. For example, a
  13416. SpecialButton class may wish to distinguish itself from a normal Button class
  13417. by adding an extra frame around the button drawn on the screen. To do that, it
  13418. uses the capabilities of virtual methods to create its own Draw method. Rather
  13419. than copy the screen drawing code in the Button class, it should be able to
  13420. call the Button class's Draw method and then continue with framing the button
  13421. so drawn.
  13422. This is an important capability of virtual functions and I expect that it
  13423. wasn't explicitly written in the article because it logically followed. But
  13424. there are issues surrounding the syntax of how to call overridden methods. In
  13425. C++ you can call the overridden method by prefixing it with the parent class
  13426. name. In my example, the code would look something like this:
  13427. class Button {
  13428. public:
  13429.  
  13430. virtual void Draw(void);
  13431. ...
  13432. };
  13433. class SpecialButton : public Button {
  13434. public:
  13435. virtual void Draw(void);
  13436. ...
  13437. };
  13438. SpecialButton::Draw() {
  13439. Button::Draw();
  13440. ... // draw frame around button
  13441. }
  13442. While in this example this makes sense, it can become confusing in larger
  13443. hierarchies. For example:
  13444. class Button {
  13445. public:
  13446. virtual void Draw(void);
  13447. ...
  13448. };
  13449. class StateButton public Button {
  13450. ... // doesn't override Draw
  13451. };
  13452. class SpecialButton : public StateButton {
  13453. public:
  13454. virtual void Draw(void);
  13455. ...
  13456. };
  13457. SpecialButton::Draw() {
  13458. StateButton::Draw();
  13459. ... // draw frame around button
  13460. }
  13461. In this case, a second class is added, StateButton, which doesn't override the
  13462. Draw method. In C++, the SpecialButton::Draw method can explicitly call the
  13463. Button::Draw method, but this bypasses any virtual overrides which the
  13464. StateButton class may have defined. Therefore, it is appropriate to call the
  13465. StateButton::Draw method, even though it does not declare one! While the
  13466. reason for using the StateButton::Draw syntax rather than the Button::Draw one
  13467. can be explained, it is confusing for programmers just learning C++.
  13468. Now, leaving C++ and its complexity behind, look at the equivalent C with
  13469. Classes code for the first code snippet.
  13470. class Button {
  13471. public:
  13472. virtual void Draw(void);
  13473. ...
  13474. };
  13475. class SpecialButton inherit Button {
  13476. public:
  13477. virtual void Draw(void);
  13478. ...
  13479. };
  13480. SpecialButton::Draw() {
  13481. Button::Draw();
  13482. ... // draw frame around button
  13483. }
  13484. This syntax still has the same level of confusion inherent in it as C++.
  13485. To refresh our memories, I don't believe that C with Classes should not
  13486. provide a similar capability. As I said, the ability to call overridden
  13487. virtual methods provides most of the power of polymorphism.
  13488. Instead of the class::method syntax, I would like to propose a syntax I first
  13489. ran into in Object Pascal. (Don't throw it out just because it has Pascal
  13490. roots.) The use of the keyword inherited allows for calling the immediately
  13491. overridden virtual method. In this case, the code for the SpecialButton::Draw
  13492. would look like:
  13493. SpecialButton::Draw() {
  13494. inherited Draw();
  13495. ... // draw frame around button
  13496. }
  13497. Or some similar syntax. This makes it clear that the programmer knows that the
  13498. override function is being called, ties the method of calling such a function
  13499. in with the inherit keyword in the class declaration, and eliminates the
  13500. confusion about which class name to prefix a overridden method with.
  13501. There are some implications to what I just proposed. First, this syntax will
  13502. only work because C with Classes implements single inheritance. Second, a
  13503. virtual method can only call the full chain of overridden methods. (I am not
  13504. sure if this is true, but in C++ it appears that you can bypass a level of
  13505. virtual functions by using an older class in the explicit
  13506. class-name::method-name syntax.)
  13507. For what it's worth, I feel that this method of calling overridden virtual
  13508. functions fits with C's feel better than the C++ syntax.
  13509. Steven Kienle
  13510. sckienle@pwinet.upj.com
  13511. P.S. I feel compelled to remind all programmers that foobar is not the correct
  13512. spelling of the word. Fubar is, as it is an acronym for "Fouled Up Beyond All
  13513. Recognition." [Fouled is a euphemism -- pjp]
  13514.  
  13515. Your concern is noted, along with many others. I repeat for emphasis that the
  13516. goal of revising Standard C is to incorporate existing practice where it makes
  13517. sense to do so, not to invent. -- pjp
  13518. Dear Mr. Plauger,
  13519. I completely agree with Bob Jervis's proposal on the evolution of C, and with
  13520. your comment.
  13521. I have been using C before, and I am using C++ now. Well, I must admit that I
  13522. am actually only using a subset of C++, and should prefer to work with a less
  13523. powerful but simpler, more reliable, and more predictable compiler.
  13524. Now, having read Jervis's article, I believe that the proposed updated C is
  13525. the language I need, and hope that we shall be able to use such a language as
  13526. soon as possible.
  13527. Please, keep us informed about it. Thank you.
  13528. Sincerely,
  13529. Emilio Morello
  13530. Via Cantore 79
  13531. 10095 Grugliasco (To)
  13532. Italy
  13533. Dear Sirs;
  13534. Re: C Standard Effort "All is Flux"
  13535. There are many "features" within the C language that need improving. I believe
  13536. that C was in danger of death when the C committee met to establish the
  13537. standard for C in 1985. It was nearly impossible to successfully write a
  13538. production program of one million lines in C. The lead time between versions
  13539. grew toward two years typically. Projects got dropped. Doom and gloom were the
  13540. norms. Weird coding conventions like the "Hungarian" got invented and adopted.
  13541. Bugs proliferated and "Lints" multiplied. Then the committee adopted function
  13542. prototypes and type checking. The committee adopted additional stern measures
  13543. and C survived.
  13544. Now we should complete the typing of the C language. Make the old K&R style
  13545. functions illegal. Remove the int default type specifier. Make the language
  13546. fully typed. Make it possible for the compiler to kill the bugs. Too many
  13547. programmers are ignoring the warnings generated by the compiler. It drives me
  13548. to tears to read some code.
  13549. Avast! I still see many compilers making new functions from improperly
  13550. constructed casts. Casts! Aghast! Casts are currently the most abused and
  13551. error causing feature of C. The C++ committee has seen the same defect. I say
  13552. adopt the identical solution. Make casts explicit so that one can read them
  13553. correctly six months after writing them. Make them so that the compiler can
  13554. identify errors of syntax, instead of labeling them as casts.
  13555. C has drawn to it programmers from other languages. I see published code that
  13556. looks like COBOL! I won't mention the name of an MS Windows guru whose code
  13557. examples begin with a long list of global function prototypes and defines. Not
  13558. to mention that this style means flipping pages of code to find the
  13559. prototypes. (Yea, some of us have to read this code stuff.) Localize these
  13560. functions to some blocks of code. Put them into libraries or headers and let
  13561. us use the "namespaces" feature of C++, or come up with a better solution!
  13562. Then, also, the compiler sometimes calls the wrong function. It is the name
  13563. scope role biting again. Or should we say, the lack of enough rules.
  13564. Namespaces is the feature that I am calling for in this complaint. We can make
  13565. purchased libraries easier to use. With this scope addition, we need type safe
  13566. linking. Since many linkers fail to complain of external functions defined
  13567. differently in two or more translation units, the whole portable C development
  13568. environment needs deliberation. It would help to separate linkage and memory
  13569. allocation identifiers so that keywords like static do not have two different
  13570. meanings. At least, we can adopt the C++ const function ideal to replace the
  13571. preprocessor #define. It surely would make for easier reading of source code.
  13572. With this const comes its anti, the mutable. I know that the powers that be
  13573. require the C++ committee to give to the C committee a listing of
  13574. incompatibilities. Many above ideals can reduce this list without breaking
  13575. existing code. (And what it does break, that code needs fixing even now.) The
  13576. C committee can see if adopting others won't break existing code.
  13577. These above ideals all reduce coding errors (unplanned "features"). Compiler
  13578. writers can carry out the above in C without making C be like C++. Speaking of
  13579. C++, we don't need another C++ language. If we add classes to C, we change it
  13580. into a caricature of C++. As most C++ compilers come with either a C compiler
  13581. included or a compatibility mode built-in, augmenting C with C++ object
  13582. supporting features wastes our money. Indubitably, IBM and Microsoft and
  13583. others have already invented pseudo-object mechanisms using C with their SOM
  13584. and COM and CORBA technology. The act of adding classes to C would not make C
  13585. classes usable with this existing technology. C with classes is C++. This
  13586. kindly ideal only duplicates the wheel.
  13587. Sincerely,
  13588. Willlam L. Hartzell
  13589. Garland, Texas 75044
  13590. Post Script:
  13591. I received the November 1994 issue just as I was about to mail this. Therein,
  13592. I found that the C committee is going to do about what I am asking. However, I
  13593. decided to send this on as my reinforcement to their efforts. Tallyho! Please
  13594. forward.
  13595. We won't have to worry about a shortage of opinions on how to revise Standard
  13596. C. -- pjp
  13597. Dear Mr. Plauger:
  13598. I've enjoyed this magazine (CUJ -- how do we acronym it now? CUJ++, perhaps?),
  13599. and your writing in general, for many years. Thanks for much useful
  13600. information on many aspects of computer science, especially my favorite
  13601. subjects, C and C++. BTW, I also like your science fiction!
  13602. I recently wrote an improved (IMHO) replacement for the Windows API function
  13603. TabbedTextOut, which required me to tokenize the output string so that each
  13604. token could be drawn at the appropriate tab stop. Naturally, I turned to the C
  13605. run-time function strtok to perform the tokenization, and discovered something
  13606. which I found surprising:
  13607. The function (I tried a few different versions) won't return a zero-length
  13608. string when it finds two delimiters together. Example:
  13609. "token 1\ttoken 2\t\ttoken 4"
  13610. I anticipated (and required) that strtok would return these four strings:
  13611. "token 1"
  13612. "token 2"
  13613. ""
  13614. "token 4"
  13615. However, (no surprise to you, I'm sure, since the version you published does
  13616. this also), it only returned the three strings of non-zero length. Since the
  13617. strings my program displays are user-defined and based on data coming from a
  13618. database populated by a variety of programs, the possibility exists (actually,
  13619. the certainty exists) that any (or several) field(s) could be zero-length.
  13620. So, I had to write my own version which returned the four strings I needed.
  13621. (No big deal, but I wish I'd found your book earlier, as it appears that
  13622. simply commenting out a single line of your version of strtok would provide
  13623. the behavior I expected).
  13624. Also, I haven't found any description of strtok which describes the behavior
  13625. at this level of granularity. My questions:
  13626. 1. Do all strtoks behave this way, and have they always? I seem to remember
  13627. doing just the opposite years ago; writing a strtok which did skip the
  13628. zero-length strings because the library version didn't. If this memory is
  13629. accurate, it was probably the Aztec compiler for the 6502, my first C compiler
  13630. (sigh).
  13631. 2. In discussion with a colleague, we wondered if this behavior was provided
  13632. (or altered) to help handle free-form language parsing?
  13633. 3. Why doesn't strtok provide this seemingly useful behavior as an
  13634. alternative? Or another run-time function which provides the zero-length
  13635. strings?
  13636. Or, am I just confused? :-)
  13637. Later,
  13638. Kit Kauffmann
  13639. kitk@mudshark.sunquest.com
  13640. 73363,447 (Compuserve)
  13641. We didn't explicitly change the behavior of strtok when we standardized it. On
  13642. the other hand, there may have been some variation among implementations in
  13643. the field. One of the desirable effects of standardization is to call
  13644. attention to such variations and encourage vendors to eliminate them. (And, by
  13645. the way, we've decided to keep the acronym for the magazine as CUJ.) -- pjp
  13646. PJP,
  13647. I have a letter here that is of a different concern. I need your help and the
  13648. readers help. I am a young programmer, just started programming in C, and am
  13649. interested in creating arcade style games. However the examples that came with
  13650. my Borland Turbo C compiler (BGIDEMO.C) are very low-level graphics. (I find I
  13651. can do the same in Quick Basic.) I really have a strong desire to learn
  13652. high-level graphics (CD-ROM quality) programming, yet I cannot find any books
  13653. on the subject. If there are any high-level graphics programmers out there
  13654. please send me some kind of info on the subject. I would really appreciate
  13655. some source code in C so I can learn (on disk or documented) how to do this.
  13656. Whoever gives me the key will have my eternal programming gratitude.
  13657. Thanks,
  13658. Dan Strohschein
  13659. 5822 Cassandra Dr.
  13660. San Bernardino, CA 92407
  13661. P.S. I mean high-level graphics and palette like the cover of C Users Journal,
  13662. May 1994.
  13663. This is not my forté. Anybody out there in the market for some eternal
  13664. gratitude? -- pjp
  13665.  
  13666.  
  13667.  
  13668.  
  13669.  
  13670.  
  13671.  
  13672.  
  13673.  
  13674.  
  13675.  
  13676.  
  13677.  
  13678.  
  13679.  
  13680.  
  13681.  
  13682.  
  13683.  
  13684.  
  13685.  
  13686.  
  13687.  
  13688.  
  13689.  
  13690.  
  13691.  
  13692.  
  13693.  
  13694.  
  13695.  
  13696.  
  13697.  
  13698.  
  13699.  
  13700.  
  13701.  
  13702.  
  13703.  
  13704.  
  13705.  
  13706.  
  13707.  
  13708.  
  13709.  
  13710.  
  13711.  
  13712.  
  13713.  
  13714.  
  13715.  
  13716.  
  13717.  
  13718.  
  13719.  
  13720.  
  13721.  
  13722.  
  13723.  
  13724.  
  13725.  
  13726.  
  13727.  
  13728.  
  13729.  
  13730.  
  13731.  
  13732.  
  13733. "Olympic" Filtering for Noisy Data
  13734.  
  13735.  
  13736. Bob Stout
  13737.  
  13738.  
  13739. Bob Stout is Director of Software development for Bridgeway Software in
  13740. Bellaire, Texas. Familiar as an ex-moderator of the FidoNet C language
  13741. EchoMail conference and archivist for the "SNIPPETS" collection of free
  13742. Csource code, Bob is also the author of 28 DOS shareware utilities plus
  13743. libraries for the Symantec, Watcom, Microsoft, and Borland C and C++
  13744. compilers, published by his own company, MicroFirm. Contact him at P.O. Box
  13745. 428, Alief, TX 77411; bobstout@neosoft.com; or Fidonet 1:06/2000.6.
  13746.  
  13747.  
  13748.  
  13749.  
  13750. The Real World
  13751.  
  13752.  
  13753. Compared to the lowly office computer, the typical embedded system lives in a
  13754. harsh environment. Embedded systems must often interact with events and
  13755. processes occuring beyond the nearest desktop, or outside the safety of the
  13756. computer room. The real world works on non-digital principles, so embedded
  13757. systems typically perform some sort of analog data acquisition, and that
  13758. analog data is subject to noise. For embedded system to perform appropriately,
  13759. noise in analog input data streams must therefore be filtered.
  13760. In this article I present a simple filtering technique that will work on a
  13761. small CPU, and that is especially effective with impulse noise -- one of the
  13762. most common and most pernicious types. This technique is not a cure-all, but
  13763. with it you may avoid much more expensive solutions to your noise problems
  13764. that would amount to overkill.
  13765.  
  13766.  
  13767. Need a Short Cut?
  13768.  
  13769.  
  13770. We already have lots of fixes for noise, both in hardware and in software. The
  13771. list of available hardware includes traditional analog filters, EMI filters,
  13772. and Digital Signal Processing (DSP) chips. These are all great solutions if
  13773. they are already installed on your embedded system. If you have to add these
  13774. components, they stop looking so attractive.
  13775. Fancy algorithms seem appealing, at least on the surface. Over the years, many
  13776. standard types of signal filters have been synthesized in software. These
  13777. software filters will kill the noise, but they require a powerful CPU. Herein
  13778. lies a problem. Many embedded systems still rely on 16- and even eight-bit
  13779. CPUs because of their low cost and low-power operation. Any but the most
  13780. trivial digital filtering techniques can easily use up much of the available
  13781. bandwidth of these smaller CPUs, leaving little time left over for doing the
  13782. work for which they were intended.
  13783. The same hardware limitation applies to Finite Impulse Response (FIR),
  13784. Infinite Impulse Response (IIR), correlation, and frequency-domain analysis.
  13785. Computing correlations and frequency-domain analyses in real time actually
  13786. requires more CPU horsepower than most of us have in our desktop systems! The
  13787. above discussion raises the question: is there a short cut? Can a non-hardware
  13788. filter remove noise without choking an embedded system CPU? The answer is yes,
  13789. in some cases.
  13790.  
  13791.  
  13792. Enter "Olympic" Filtering
  13793.  
  13794.  
  13795. The noise in analog data will typically be one of two types, AC or impulse.
  13796. You can hear both of these types of noise in your own home audio system. An
  13797. example of AC noise is the line frequency hum heard when some component is
  13798. poorly grounded or shielded. An example of impulse noise is when someone turns
  13799. on a high-current appliance and you hear a pop or snap though the speakers.
  13800. Averaging can work well with AC noise. By careful selection of the sampling
  13801. frequency and filter interval, the effects of an interfering signal may be
  13802. readily removed. However, impulse noise will still affect averaging in an
  13803. unpredictable manner. Using a weighted average can improve the filter's
  13804. responsiveness and rejection of AC noise, but again does little to help with
  13805. impulse noise.
  13806. What to do?
  13807. Many years ago I borrowed an idea from the Olympics, where an athlete's scores
  13808. were determined by throwing out the highest and lowest scores from the panel
  13809. of judges and averaging the rest. I asked myself if I could eliminate my "bad"
  13810. data in the same manner. Implemented in assembly language, the resulting
  13811. algorithm was simple and proved to be remarkably effective. In the years
  13812. since, I've used this same simple filtering technique successfully on dozens
  13813. of embedded projects in several different languages. This technique has worked
  13814. especially well in equipment mounted adjacent to, and powered from, the same
  13815. power lines as 60+ HP electric motors that are switched on and off
  13816. asynchronously at frequent intervals. It has also worked well in environments
  13817. with analog inputs derived from potentiometer sensors, which get increasingly
  13818. noisy over time.
  13819. Why does olympic filtering work? All averaging filters depend on oversampling,
  13820. i.e., you always have more data than you really need. When averaging, the
  13821. redundancy in the "good" data makes a more significant contribution to the
  13822. result than does the anomalous data. Any additive noise having a mean value of
  13823. zero -- such as AC noise -- is a candidate for an averaging filter. Impulse
  13824. noise presents special problems, however. Impulse noise is intermittent and,
  13825. when it does occur, typically is not additive. Noise spikes tend to replace
  13826. normal data rather than simply inducing an offset. Olympic filtering
  13827. identifies probable anomalous data and removes it before the average is
  13828. calculated. If there is no anomalous data, then the olympic filter responds
  13829. the same as a simple averaging filter. Its only added cost is a requirement
  13830. for oversampling, which provides two more samples per averaging period that
  13831. will never actually be used.
  13832.  
  13833.  
  13834. Implementation
  13835.  
  13836.  
  13837. Implementing an olympic filter is simple. Store the acquired data in a simple
  13838. circular buffer. To obtain an output value, sum the data, subtract out the
  13839. high and low values, and divide the sum by the buffer size minus two. I've
  13840. always used buffer sizes that were two plus an integral power of two, so that
  13841. after removing the high and low values, I could calculate the average by
  13842. shifting rather than division.
  13843. In Listing 1 and Listing 2 I show a standard C implementation, since good
  13844. quality C compilers are available for virtually any target platform you might
  13845. choose. However, for many low-end CPUs, assembly language might be a better
  13846. choice. I think that for embedded PCs, C++ would clearly be superior.
  13847. (Circular buffer management in general, and filtering algorithms in
  13848. particular, are especially well suited to a C++ implementation when writing
  13849. for embedded platforms with available C++ compilers. Although omitted here for
  13850. space, the following files are available on this month's code disk and online
  13851. sources: CIRCBUF.HPP, a template-based circular-buffer class; OLYMFILT.HPP, an
  13852. olympic filter derived from the circular-buffer base class; AVGFILT.HPP,
  13853. another straight averaging filter for comparison, also derived from the
  13854. circular-buffer base class; and CIRCBUF.CPP, a demonstration program.) The
  13855. underlying algorithm retains its efficiency regardless of target environment
  13856. or implementation tools.
  13857. Listing 1 shows the header file CIRCBUF.H. Note that the buffer is embedded in
  13858. a struct object containing a pointer to the actual buffer. This struct also
  13859. contains the buffer's size, a pointer to the next location to fill, and a flag
  13860. to indicate whether the buffer has been filled. This last variable is
  13861. important. In any filtering algorithm, running the filter before the buffer
  13862. has been filled will greatly skew the result. The filter output will only be
  13863. valid to the extent that the buffer data are valid.
  13864. CIRCBUF.C (Listing 2) defines the three functions essential to the filter's
  13865. implementation: allocating a buffer of the required size, adding data to it,
  13866. and running the olympic filter on it. CIRCBUF.C also includes a straight
  13867. averaging filter for comparison and a sample main function which may be used
  13868. for testing by simply defining the macro TEST.
  13869. Both the olympic and averaging filters take a "snapshot," copying the buffer
  13870. to temporary storage. In a typical system, the sample acquisition (and hence
  13871. the add function) will run at intervals based on timer interrupts. Since these
  13872. interrupts may occur asynchronously, good coding practice dictates that you
  13873. somehow stabilize the data set before processing. Other alternatives would be
  13874. to simply disable the timer interrupt while processing the buffer, or to
  13875. perform data acquisition synchronously with other tasks. As written, the code
  13876. presented really needs modification to disable the timer interrupts while
  13877. taking the snapshot.
  13878.  
  13879.  
  13880. Testing and Tuning
  13881.  
  13882.  
  13883. Compiling the sample demo with the TEST macro defined easily demonstrates the
  13884. differences between olympic and "straight" averaging filters. Using
  13885. well-behaved data and a six sample buffer size results in comparable
  13886. performance from the two filters. For example, entering
  13887. CIRCBUF 1 2 3 4 5 6 7 8 9 10
  13888. shows no difference between the results of the two types of filters, whereas
  13889. entering
  13890. CIRCBUF 3 4 4 5 -200 1000
  13891. shows dramatically different results typical of using an olympic filter in the
  13892. presence of significant impulse noise.
  13893. Olympic filtering is clearly superior to an averaging filter on data
  13894. containing significant impulse noise. Like other averaging filters, olympic
  13895. filters can also be tuned for good performance in the presence of AC noise by
  13896. setting a sample rate equal to 1/(noise_frequency * buffer_size). For example,
  13897. if you are using an olympic buffer containing six samples, a sampling interval
  13898. of 2.78 msec. will provide your system with good immunity from induced 60 Hz
  13899. noise. Choice of buffer size also dramatically affects the outcome. Small
  13900. buffers reject the most noise and respond to changing conditions more quickly,
  13901. at the expense of potentially discarding the most good data. Large buffers
  13902. take longer to fill and respond more slowly, but eliminate more AC noise.
  13903.  
  13904.  
  13905.  
  13906. Give it a Try
  13907.  
  13908.  
  13909. No one type of signal filtering works for all applications. I've briefly
  13910. discussed some of the most common algorithms and their suitability to smaller
  13911. embedded systems projects. The olympic filter is a simple algorithm with a
  13912. successful track record operating in some of the noisiest environments
  13913. imaginable. Although I've never read of this algorithm being published
  13914. elsewhere, it's been in my bag of tricks for years. Try it on your next
  13915. embedded project when noise is a problem. You may find, as I often have, that
  13916. it's all you need.
  13917.  
  13918. Listing 1 Header file for circular buffer C functions
  13919. #ifndef _CIRCBUF_DEFINED_____LINEEND____
  13920. #define _CIRCBUF_DEFINED_____LINEEND____
  13921.  
  13922. typedef enum {CSIZ_TINY = 4, CSIZ_SMALL = 6, CSIZ_MEDIUM = 10,
  13923. CSIZ_LARGE = 18, CSIZ_HUGE = 34} CSIZE;
  13924.  
  13925. typedef enum {FALSE, TRUE} LOGICAL;
  13926.  
  13927. typedef struct {
  13928. CSIZE len;
  13929. size_t next;
  13930. LOGICAL full;
  13931. short *buf;
  13932. } CBUF;
  13933.  
  13934. CBUF *cbuf_malloc(CSIZE);
  13935. LOGICAL cbuf_add(CBUF *, short);
  13936.  
  13937. short OlympicFilt(CBUF *);
  13938. short AverageFilt(CBUF *);
  13939.  
  13940. #ifndef MAX
  13941. #define MAX(a,b) (((a) > (b)) ? (a) : (b))
  13942. #endif
  13943.  
  13944. #ifndef MIN
  13945. #define MIN(a,b) (((a) < (b)) ? (a) : (b))
  13946. #endif
  13947.  
  13948. #endif // _CIRCBUF_DEFINED_____LINEEND____
  13949.  
  13950. /* End of File */
  13951.  
  13952.  
  13953. Listing 2 Circular buffer C functions
  13954. #include <stdlib.h>
  13955. #include <limits.h>
  13956. #include "circbuf.h"
  13957.  
  13958. /**********************************************************************/
  13959. /* */
  13960. /* cbuf_malloc() - Function to allocate a circular buffer on the heap.*/
  13961. /* */
  13962. /* Arguments: 1 - Size of buffer. */
  13963. /* */
  13964. /* Returns: Pointer to buffer struct or NULL upon failure. */
  13965. /* */
  13966. /**********************************************************************/
  13967.  
  13968. CBUF *cbuf_malloc(CSIZE size)
  13969. {
  13970. CBUF *ptr;
  13971.  
  13972.  
  13973. if (NULL != (ptr = calloc(sizeof(CBUF) + (size * sizeof(short)), 1)))
  13974. {
  13975. ptr->buf = (short *)((char *)ptr + sizeof(CBUF));
  13976. ptr->len = size;
  13977. return ptr;
  13978. }
  13979. else return NULL;
  13980. }
  13981.  
  13982. /**********************************************************************/
  13983. /* */
  13984. /* cbuf_add() - Function to add data to a circular buffer. */
  13985. /* */
  13986. /* Arguments: 1 - Pointer to circular buffer struct. */
  13987. /* 2 - Data to add. */
  13988. /* */
  13989. /* Returns: TRUE if buffer has been filled, else FALSE. */
  13990. /* */
  13991. /**********************************************************************/
  13992.  
  13993. LOGICAL cbuf_add(CBUF *cbuf, short data)
  13994. {
  13995. cbuf->buf[cbuf->next]= data;
  13996. if (cbuf->len <= ++(cbuf->next))
  13997. {
  13998. cbuf->next = 0;
  13999. cbuf->full = TRUE;
  14000. }
  14001. return cbuf->full;
  14002. }
  14003.  
  14004. /**********************************************************************/
  14005. /* */
  14006. /* OlympicFilt() - Function to perform an "Olympic" filter on the */
  14007. /* data in a circular buffer. */
  14008. /* */
  14009. /* Arguments: 1 - Pointer to circular buffer struct. */
  14010. /* */
  14011. /* Returns: TRUE if buffer has been filled, else FALSE. */
  14012. /* */
  14013. /**********************************************************************/
  14014.  
  14015. int OlympicFilt(CBUF * cbuf)
  14016. {
  14017. size_t i;
  14018. long accum;
  14019. short cmin = SHRT_MAX, cmax = SHRT_MIN;
  14020. short obuf[CSIZ_HUGE * sizeof(short)];
  14021.  
  14022.  
  14023. /*
  14024. ** The buffer may be subject to asynchronous modification,
  14025. ** so take a snapshot of it.
  14026. */
  14027.  
  14028. memcpy(obuf, cbuf->buf, cbuf->len * sizeof(short));
  14029.  
  14030. for (i = 0, accum = 0L; i < cbuf->len; ++i)
  14031. {
  14032.  
  14033. accum += obuf;
  14034. cmin = MIN(obuf[i], cmin);
  14035. cmax = MAX(obuf[i], cmax);
  14036. }
  14037. accum -= cmin;
  14038. accum -= cmax;
  14039. switch (cbuf->len)
  14040. {
  14041. case CSIZ_TINY:
  14042. return (short)(accum >> 1);
  14043.  
  14044. case CSIZ_SMALL:
  14045. return (short)(accum >> 2);
  14046.  
  14047. case CSIZ_MEDIUM:
  14048. return (short)(accum >> 3);
  14049.  
  14050. case CSIZ_LARGE:
  14051. return (short)(accum >> 4);
  14052.  
  14053. case CSIZ_HUGE:
  14054. return (short)(accum >> 5);
  14055.  
  14056. default:
  14057. return(short)(accum / (cbuf->len - 2));
  14058. }
  14059. }
  14060.  
  14061. /**********************************************************************/
  14062. /* */
  14063. /* AverageFlit() - Function to filter the data in a circular buffer */
  14064. /* by simple averaging. */
  14065. /* */
  14066. /* Arguments: 1 - Pointer to circular buffer struct. */
  14067. /* */
  14068. /* Returns: TRUE if buffer has been filled, else FALSE. */
  14069. /* */
  14070. /**********************************************************************/
  14071.  
  14072. short AverageFilt(CBUF * cbuf)
  14073. {
  14074. size_t i;
  14075. long accum;
  14076. short obuf[CSIZ_HUGE * sizeof(short)];
  14077.  
  14078. /*
  14079. ** The buffer may be subject to asynchronous modification,
  14080. ** so take a snapshot of it.
  14081. */
  14082.  
  14083. memcpy(obuf, cbuf->buf, cbuf->len * sizeof(short));
  14084.  
  14085. for (i = 0, accum = 0L; i < cbuf->len; ++i)
  14086. accum += obuf[i];
  14087.  
  14088. return(short)(accum / (cbuf->len));
  14089. }
  14090.  
  14091. #ifdef TEST
  14092.  
  14093.  
  14094. #include <stdio.h>
  14095.  
  14096. int main(int argc, char *argv[])
  14097. {
  14098. int i, n;
  14099. CBUF *cbuf = cbuf_malloc(CSIZ_SMALL);
  14100.  
  14101. while (--argc)
  14102. {
  14103. n = atoi(*++argv);
  14104. printf("Adding %d returned %d\n", n, cbuf_add(cbuf, n));
  14105. }
  14106. printf("\nOlympic average is %d\n", OlympicFilt(cbuf));
  14107. printf("\nStraight average is %d\n", AverageFilt(cbuf));
  14108. return EXIT_SUCCESS;
  14109. }
  14110.  
  14111. #endif
  14112.  
  14113. /* End of File */
  14114.  
  14115.  
  14116.  
  14117.  
  14118.  
  14119.  
  14120.  
  14121.  
  14122.  
  14123.  
  14124.  
  14125.  
  14126.  
  14127.  
  14128.  
  14129.  
  14130.  
  14131.  
  14132.  
  14133.  
  14134.  
  14135.  
  14136.  
  14137.  
  14138.  
  14139.  
  14140.  
  14141.  
  14142.  
  14143.  
  14144.  
  14145.  
  14146.  
  14147.  
  14148.  
  14149.  
  14150.  
  14151.  
  14152.  
  14153.  
  14154.  
  14155.  
  14156. When the "Best" Algorithm Isn't
  14157.  
  14158.  
  14159. Blase B. Cindric
  14160.  
  14161.  
  14162. Currently in his tenth year of teaching computer science at the undergraduate
  14163. level, Blase has spent the past six years as a faculty member at Westminster
  14164. College, New Wilmington, PA. In addition to his full-time teaching
  14165. responsibilities, he is a part-time Ph.D. student in computer science at the
  14166. University of Pittsburgh, where his areas of interest are theory of
  14167. computation and algorithmic analysis. He holds a Masters degree in computer
  14168. science from Penn State, lives very happily with his wife, and is at the beck
  14169. and call of their two cats. He can be reached via e-mail as
  14170. bbc@cheese.westminster.edu.
  14171.  
  14172.  
  14173.  
  14174.  
  14175. Introduction
  14176.  
  14177.  
  14178. When computer scientists analyze the performance of algorithms, they attempt
  14179. to make the analysis as general as possible by focusing on large data sets.
  14180. Also, no assumptions are made concerning any properties that the data might
  14181. have. The idea behind this approach is to make the resulting rankings of the
  14182. algorithms valid for the largest variety of data possible.
  14183. So, for instance, the consensus of the computer science community is that the
  14184. best sorting algorithm, on average, is the Quicksort algorithm. A programmer
  14185. without much experience who sees such a claim is likely to accept the wisdom
  14186. of the ancients as the whole truth and nothing but the truth. But there is a
  14187. catch. We programmers are always working on some specific problem, and
  14188. sometimes we have extra information about our data sets that may allow us to
  14189. obtain better performance than the "best" algorithm. For our purposes, we can
  14190. do better than the "best."
  14191. Such a situation arose in an application I developed several years ago. I
  14192. present here a simplified version. We have an array that must contain unique
  14193. values (no duplicates allowed), with the currently used positions of the array
  14194. containing sorted values. After the end of this sorted data, several unsorted
  14195. additions are made to the array.
  14196. Our task is to sort the array so the entire data set is sorted. So we have an
  14197. array that is mostly sorted, with some unsorted stuff at the end. In this
  14198. situation, it turns out that there is a sorting method that outperforms
  14199. Quicksort for some array configurations, a variant of the insertion sort
  14200. routine.
  14201.  
  14202.  
  14203. When Simpler is Better
  14204.  
  14205.  
  14206. Fans of algorithmic analysis may balk at this claim. "How can lowly insertion
  14207. sort, whose average behavior is O(n2), outperform the mighty Quicksort
  14208. routine, with its O(n*log n) average time?" The secret lies in the fact that
  14209. if we treat the array as two subarrays, one part sorted and the other part
  14210. containing data to be inserted in order into the sorted part, then our
  14211. algorithm need only perform a few insertions into the sorted part to
  14212. accomplish our sorting task.
  14213. Quicksort will process the entire array, without exploiting the fact that most
  14214. of the sorting task is already done. For certain proportions of the size of
  14215. the sorted and unsorted portions of the array, this insertion sort method
  14216. outperforms Quicksort.
  14217. Listing 1 contains source code for this special insertion sort routine. This
  14218. code assumes that the array contains unsigned integers, and is to be sorted in
  14219. ascending order. The idea is simple. For each element in the unsorted portion
  14220. of the array, first copy the value into a temporary variable. Then work
  14221. backwards through the sorted portion of the array, copying each value that is
  14222. larger than the item to be inserted down one slot in the array.
  14223. When control exits the inner for loop, list[j] is the array element that
  14224. contains the largest value in the array that is not larger than the item to be
  14225. inserted, and list[j+1] has been copied into list[j+2], with all subsequent
  14226. values following in sequence. In essence, the code has moved all of the values
  14227. greater than our item down one position in the array, opening up a slot
  14228. (1ist[j+1]) where that value should be placed to maintain sorted order.
  14229.  
  14230.  
  14231. Performance
  14232.  
  14233.  
  14234. This special insertion sort routine performs best when the number of
  14235. insertions to be made is small. As the size of the unsorted portion of the
  14236. array grows relative to the sorted portion, the performance advantage
  14237. decreases. Eventually, for a certain number of unsorted additions, the time
  14238. taken to painstakingly move each unsorted value into its rightful place
  14239. exceeds the time needed if we ignore our special array properties and just use
  14240. Quicksort. This crossover point depends in each case on the sizes of the two
  14241. portions.
  14242. I've run tests for different array sizes and proportions of sorted to unsorted
  14243. elements, with the results shown in Table 1. Roughly speaking, if the number
  14244. of unsorted elements to be inserted is less than 2 percent of the size of the
  14245. sorted portion, then this special sort method gives us a performance advantage
  14246. over blindly using Quicksort. The solution I employed for my application
  14247. calculates this proportion, and uses this special insertion sort technique if
  14248. the number of additions is below this 2 per cent threshold. It uses Quicksort
  14249. if there are lots of additions to be made.
  14250. The whole point here is that knowledge of special characteristics of the data
  14251. set to be processed can alter the choice of algorithm that yields the best
  14252. performance in practice. The moral of this story? Take advantage of any
  14253. special properties you know about the data set when choosing an algorithm, and
  14254. you too can do better than the "best."
  14255. Table 1 Sizes of sorted and unsorted portions of arrays. For each table entry,
  14256. a smaller unsorted portion results in the special insertion sort outperforming
  14257. Quicksort, while a larger unsorted portion makes Quicksort the better
  14258. performer.
  14259.  Size of Size of
  14260.  Sorted Unsorted
  14261. Size of Array Portion Portion Proportion
  14262. --------------------------------------------
  14263.  500 489 11 2.25%
  14264.  1000 979 21 2.15%
  14265.  2000 1952 48 2.46%
  14266.  5000 4900 100 2.04%
  14267.  7500 7368 132 1.79%
  14268.  10000 9812 188 1.92%
  14269.  15000 14692 308 2.10%
  14270.  20000 19564 436 2.23%
  14271.  
  14272. Listing 1 Special insertion sort routine
  14273. /*-----------------------------*
  14274. * Special Insertion Sort *
  14275. * (c) 1994, Blase B. Cindric *
  14276. * All rights reserved, and *
  14277. * jealously guarded *
  14278. *-----------------------------*/
  14279.  
  14280.  
  14281. void special_insertion_sort(
  14282.  
  14283. unsigned *list, /* array to be sorted*/
  14284. int n, /* no, of elements in array */
  14285. int num_sorted) /* no. of elements already in order */
  14286.  
  14287. {
  14288.  
  14289. int j, k;
  14290. unsigned item_to_place;
  14291.  
  14292. /*------------------------------------------*
  14293. * subscripts for sorted portion of array: *
  14294. * 0 to (num_sorted - 1) *
  14295. * subscripts for unsorted portion: *
  14296. * num_sorted to (n - 1) *
  14297. *------------------------------------------*/
  14298.  
  14299. for (k = num_sorted; k < n; k++) {
  14300.  
  14301. /* move new item out of array */
  14302.  
  14303. item_to_place = list[k];
  14304.  
  14305. /* copy all values larger than new item down one place in the array */
  14306.  
  14307. for (j = k - 1; list[j] > item_to_place && j >= 0; j--)
  14308. list[j+1] = list[j];
  14309.  
  14310. /* place new item in its proper array position */
  14311.  
  14312. list[j+1] = item_to_place;
  14313.  
  14314. } /* end of outer for loop */
  14315.  
  14316. } /* end of special insertion sort */
  14317.  
  14318. /* End of File */
  14319.  
  14320.  
  14321.  
  14322.  
  14323.  
  14324.  
  14325.  
  14326.  
  14327.  
  14328.  
  14329.  
  14330.  
  14331.  
  14332.  
  14333.  
  14334.  
  14335.  
  14336.  
  14337.  
  14338.  
  14339.  
  14340.  
  14341.  
  14342.  
  14343.  
  14344. Octree Color Quantization
  14345.  
  14346.  
  14347. Ian Ashdown
  14348.  
  14349.  
  14350. This article is not available in electronic form.
  14351.  
  14352.  
  14353.  
  14354.  
  14355.  
  14356.  
  14357.  
  14358.  
  14359.  
  14360.  
  14361.  
  14362.  
  14363.  
  14364.  
  14365.  
  14366.  
  14367.  
  14368.  
  14369.  
  14370.  
  14371.  
  14372.  
  14373.  
  14374.  
  14375.  
  14376.  
  14377.  
  14378.  
  14379.  
  14380.  
  14381.  
  14382.  
  14383.  
  14384.  
  14385.  
  14386.  
  14387.  
  14388.  
  14389.  
  14390.  
  14391.  
  14392.  
  14393.  
  14394.  
  14395.  
  14396.  
  14397.  
  14398.  
  14399.  
  14400.  
  14401.  
  14402.  
  14403.  
  14404.  
  14405. An Introduction to Genetic Algorithms
  14406.  
  14407.  
  14408. Keith Grant
  14409.  
  14410.  
  14411. Keith Grant is a software engineering manager at Standard & Poor's Compustat,
  14412. a provider of financial data and analysis tools. He also teaches C++ and OO
  14413. analysis and design. He has a BS in physics from the University of Washington.
  14414.  
  14415.  
  14416.  
  14417.  
  14418. Introduction
  14419.  
  14420.  
  14421. A surprising number of everyday problems are difficult to solve by traditional
  14422. algorithms. A problem may qualify as difficult for a number of different
  14423. reasons; for example, the data may be too noisy or irregular; the problem may
  14424. be difficult to model; or it may simply take too long to solve. It's easy to
  14425. find examples: finding the shortest path connecting a set of cities, dividing
  14426. a set of different tasks among a group of people to meet a deadline, or
  14427. fitting a set of various sized boxes into the fewest trucks. In the past,
  14428. programmers might have carefully hand crafted a special-purpose program for
  14429. each problem; now they can reduce their time significantly by using a genetic
  14430. algorithm.
  14431.  
  14432.  
  14433. What are Genetic Algorithms?
  14434.  
  14435.  
  14436. A genetic algorithm (GA) is one of a relatively new class of stochastic search
  14437. algorithms. Stochastic algorithms are those that use probability to help guide
  14438. their search. John Holland developed GAs at the University of Michigan in the
  14439. mid-1970s. As the name implies, GAs behave much like biological genetics. GAs
  14440. encode information into strings, just as living organisms encode
  14441. characteristics into strands of DNA. (The choice of the term string is
  14442. unfortunate. In the GA community, a string contains the potential solution and
  14443. bears no relationship to a string in C or C++.)
  14444. A string in a GA is analogous to a chromosome in biology. A population of
  14445. strings competes and those strings that are the fittest procreate, the rest
  14446. eventually die off, childless. As with biological parents, two strings combine
  14447. and contribute part of their characteristics to create their offspring, the
  14448. new individual. This new string joins the population and the fight to produce
  14449. the next generation. If both parents contribute good building blocks (short
  14450. sections of the string) to the offspring, it will be more fit and will
  14451. procreate in its turn. If the building blocks are poor then the offspring will
  14452. die off without generating offspring. A second -- but important -- process
  14453. occurs in GAs: sometimes, very rarely, a mutation occurs and the offspring
  14454. will incorporate a new building block that came from neither parent. The above
  14455. cycle of death and birth repeats until an acceptable solution to the problem
  14456. is found.
  14457.  
  14458.  
  14459. Benefits of Genetic Algorithms
  14460.  
  14461.  
  14462. One of a GA's most important qualities is its ability to evaluate many
  14463. possible solutions simultaneously. This ability, called implicit parallelism,
  14464. is the cornerstone of GA's power. Implicit parallelism results from
  14465. simultaneous evaluation of the numerous building blocks that comprise the
  14466. string. Each string may contain millions of these building blocks, and the GA
  14467. assesses them all simultaneously each time it calculates the string's fitness.
  14468. In effect, the algorithm selects for patterns inside the string that exhibit
  14469. high worth, and passes these building blocks on to the next generation. This
  14470. selection process enables genetic algorithms to perform well where traditional
  14471. algorithms flounder, such as in problems with huge search spaces.
  14472.  
  14473.  
  14474. Robustness
  14475.  
  14476.  
  14477. Genetic algorithms also have the quality of robustness. That is, while
  14478. special-case algorithms may find more optimal solutions to specific problems,
  14479. GAs perform very well over a large number of problem categories. This
  14480. robustness results in part because genetic algorithms usually apply their
  14481. search against a large set of points, rather than just a single point, as do
  14482. calculus-based algorithms. Because of this, GAs are not caught by local minima
  14483. or maxima. Another contribution to their robustness is that GAs use the
  14484. strings' fitness to direct the search; therefore they do not require any
  14485. problem-specific knowledge of the search space, and they can operate well on
  14486. search spaces that have gaps, jumps, or noise.
  14487.  
  14488.  
  14489. Miscellaneous Benefits
  14490.  
  14491.  
  14492. GAs also perform well on problems whose complexity increases exponentially
  14493. with the number of input parameters. Such problems, called NP-complete, would
  14494. take years to solve using traditional approaches. Furthermore, genetic
  14495. algorithms can produce intermediate solutions; the program can stop at any
  14496. time if a suboptimal solution is acceptable. Finally, GAs easily lend
  14497. themselves to parallel processing; they can be implemented on any
  14498. multiprocessor architecture.
  14499.  
  14500.  
  14501. The Algorithm Explored
  14502.  
  14503.  
  14504. As the pseudo-code in Figure 1 illustrates, the basic genetic algorithm is
  14505. quite simple.
  14506. While extensive research on GAs has produced no optimal implementation (many
  14507. aspects of GAs are still debated), there are several algorithms that work
  14508. quite well in most situations. The example algorithm (Figure 1) is one of
  14509. these implementations, and it is simple and reliable.
  14510. Even with an off-the-shelf GA, the programmer still faces two significant
  14511. tasks: designing the coding scheme and creating the fitness function. The
  14512. coding scheme defines how a string will represent a potential solution of the
  14513. problem at hand. The fitness function uses the coding scheme to evaluate each
  14514. string's fitness or worth. By combining these two parts the genetic algorithm
  14515. can calculate how well any string solves the problem.
  14516.  
  14517.  
  14518. The Knapsack Problem
  14519.  
  14520.  
  14521. To understand how GAs work, consider a concrete example. The knapsack problem
  14522. is a classic NP-complete problem. (Sedgewick uses this kind of problem in his
  14523. book, Algorithms [1], to illustrate dynamic programming.) Simply stated, given
  14524. a pile of items that vary in weight and value, find that combination of items
  14525. having the greatest total value but which does not exceed a maximum weight. In
  14526. other words, the goal is to fill up a hypothetical knapsack with the most
  14527. expensive loot it can carry. While it's easy to describe, this goal can be
  14528. difficult to accomplish. For example, a pile of just 50 items presents 250
  14529. different possible selections. Assuming a computer could test a million
  14530. different combinations each second, it would still take 35 years to try them
  14531. all.
  14532. In this article, I show how a GA solves such a problem, but for the sake of
  14533. illustration I use a smaller number of items. The pile contains fourteen
  14534. items, so it provides a little more than 16,000 possible combinations to try
  14535. in the knapsack. There are five different kinds of items, ranging from 3 to 9
  14536. in weight and from 4 to 13 in value. The knapsack can hold a maximum weight of
  14537. 17, so it can carry one A, or two Bs, etc. Table 1 lists all the different
  14538. items, their weights and values, and maximum number of each type that can fit
  14539. into the knapsack. The program in Listing 1 illustrates the use of a GA to
  14540. solve the knapsack problem. Supporting functions and class definitions appear
  14541. in Listing 2 through Listing 7.
  14542.  
  14543.  
  14544.  
  14545. Developing a Coding Scheme
  14546.  
  14547.  
  14548. The first step in writing a GA is to create a coding scheme. A coding scheme
  14549. is a method for expressing a solution in a string. Many successful types have
  14550. been discovered, but there is no mechanical technique for creating one. Like
  14551. programming, creating a coding scheme is part science and part art, but also
  14552. like programming, it gets easier with practice. Early researchers used binary
  14553. encoded strings exclusively, but higher order alphabets work without loss of
  14554. efficiency and power. The type of coding scheme to use depends on the problem.
  14555. Order defines the number of different characters in the alphabet. Do not
  14556. confuse the GA term character with ASCII characters. A GA character is
  14557. analogous to a gene in that it has a position and a value. A binary alphabet
  14558. has an order of two, meaning that the characters can only have two values, 0
  14559. or 1.
  14560. The coding scheme I've chosen for the knapsack uses a fixed, length, binary,
  14561. position-dependent string. The pile in the example contains fourteen items so
  14562. each string must have fourteen binary characters, one character for each item.
  14563. The location of each character in the string represents a specific item and
  14564. the value of the character indicates whether that item is in the knapsack or
  14565. left in the pile.
  14566. Figure 2 illustrates the coding of fourteen items into a GA string. The coding
  14567. scheme's equivalent in C++ is the array of struct, ItemDesc, shown in Listing
  14568. 1. Each column of the table represents a character position in the string. The
  14569. top three lines give the label, weight, and value of each character position.
  14570. The bottom three lines show strings that define potential solutions to the
  14571. knapsack problem. In this case a 1 means the item is in the knapsack and a 0
  14572. means the item is in the pile. The first string places six items into the
  14573. knapsack: one A, B, C, and D, and two Es, for a total weight of 34 and total
  14574. value of 47. The second string places five items in the knapsack: two Ds, and
  14575. three Es, for a weight of 17 and value of 22. The third string uses just two
  14576. items: one A and one E for a weight of 12 and a value of 17.
  14577.  
  14578.  
  14579. Creating a Fitness Function
  14580.  
  14581.  
  14582. The next step is to create a function that will evaluate how well each string
  14583. solves the problem -- that is, calculate the string's fitness. The knapsack
  14584. problem requires maximization of the loot's value in the knapsack. If this
  14585. were the only requirement, a fitness function could simply rank a string by
  14586. adding up the values of all the items put into the knapsack. The GA would then
  14587. tell us that the best solution was to put all 14 items in the knapsack.
  14588. However, a second requirement states that the weight of the items cannot
  14589. exceed a maximum (17, in this example). So this fitness function fails
  14590. miserably. In GA terminology, it results in a constraint violation.
  14591. GA researchers have explored many approaches to constraint violation but none
  14592. are perfect. Here are three possibilities:
  14593.  
  14594.  
  14595. Elimination
  14596.  
  14597.  
  14598. Elimination attempts to determine if a string violates the constraint before
  14599. it is ever created. This approach has several problems. For starters, it may
  14600. be too expensive to perform, or simply impossible. Second, preventing the
  14601. creation of violators may cause GA to overlook perfectly valid solutions.
  14602. That's because violators could produce legal (non-violating) offspring that
  14603. would lead to a satisfactory solution more quickly.
  14604.  
  14605.  
  14606. High Penalty
  14607.  
  14608.  
  14609. This approach imposes a high penalty on violators. It reduces violators' worth
  14610. while allowing them to occasionally propagate offspring. A weakness of this
  14611. approach becomes apparent when a population contains a large percentage of
  14612. violators. In this case, legal strings will dominate the following generations
  14613. and the violators will be left unexploited. This effect could lead to
  14614. population stagnation.
  14615.  
  14616.  
  14617. Moderate Penalty
  14618.  
  14619.  
  14620. This approach imposes a moderate penalty on violators. It increases the
  14621. probability that violators will procreate, thus reducing the chance of
  14622. population stagnation. This approach exhibits its own problems, especially
  14623. when violators rate higher than legal strings. In this case, if the violators
  14624. do not create legal strings then violators will dominate the following
  14625. generations. Furthermore, if violators rate higher than legal strings then the
  14626. criteria for ending the search must incorporate a mechanism for detecting
  14627. violators.
  14628. The knapsack example employs the third technique. Its fitness function
  14629. (CalcFitness in Listing 1) adds up the value of each item and subtracts a
  14630. moderate penalty for violators. The penalty is three times the amount of
  14631. excess weight. Table 2 shows the resulting fitness of the three example
  14632. strings previously defined.
  14633.  
  14634.  
  14635. Initialization
  14636.  
  14637.  
  14638. After the coding scheme and fitness function are integrated into the GA it is
  14639. ready to run. The GA's first task is to create an initial population of
  14640. strings. The demo program stores this population in an object (Pop) of class
  14641. CGAPopulation (defined in Listing 4). Each string in the population is an
  14642. object of class CGAChromosome (Listing 2).
  14643. There are many ways to select an initial population; approaches range from
  14644. randomly setting each character of every string to modifying the results of a
  14645. search made previously by a human. The knapsack example uses a modified
  14646. weighted random design. The initialization function (class CGAPopulation's
  14647. constructor, Listing 5) creates strings with an increasing likelihood of
  14648. setting each bit to 1. Figure 3 shows the result of creating ten strings. The
  14649. probability of setting any bit to 1 for the first string, labeled U, is 10%.
  14650. The probability increases incrementally for each new string created until all
  14651. the strings are created and the probability reaches about 50%.
  14652. After creating and initializing each string, the constructor creates a
  14653. complement of that string, by calling member function Complement (Listing 3).
  14654. The complement string has the opposite bit pattern of the original.
  14655. Note that in the top half of the table the U string contains only one one-bit,
  14656. whereas each successive string has an increasing number of one-bits, until the
  14657. fifth string has about half ones and zeros. The bottom half of the figure
  14658. shows the complement strings to the original five.
  14659. The composition of the initial population can dramatically affect the
  14660. performance of the genetic algorithm. The more diverse the initial population
  14661. the more opportunities the GA will have to exploit the search space. The above
  14662. initialization scheme has the advantage of simplicity and diversity. It is
  14663. simple in that it does not require any information about the problem. The
  14664. scheme is diverse because the function creates strings ranging from mostly
  14665. zeros to mostly ones and everything in-between.
  14666. How large should the initial population be? The population should be large
  14667. enough to create a diverse set of individuals for the GA to exploit but not so
  14668. large that creating the initial population dominates computer time. The
  14669. knapsack example sets the initial population to 30. I picked this rather small
  14670. population to better illustrate how GAs work.
  14671.  
  14672.  
  14673. Parent Selection and Procreation
  14674.  
  14675.  
  14676. After creating an initial population, the GA selects two parents for the
  14677. purpose of procreation. Parent selection is based on string fitness. While
  14678. creating the initial population, the fitness function calculates the worth of
  14679. each string. This calculation occurs within each string's constructor,
  14680. CGAChromosome::CGAChromosome (Listing 3). The population constructor
  14681. CGAPopulation::CGAPopulation then ranks the strings according to their
  14682. fitness, by calling member function Merge (Listing 5).
  14683. After the popuplation constructor returns, the main program enters a while
  14684. loop, and stays there until a solution is found. It's unlikely that any
  14685. strings in the initial generation contain a solution; if the while condition
  14686. is satisfied (no solution found), the program calls
  14687. CGAPopulation::CreateNextGeneration (Listing 5) to create a new generation of
  14688. strings.
  14689. The first step in creating a new generation is selection of two parents. The
  14690. GA does not select strings directly by their rank in the population, so the
  14691. best string is not guaranteed to be a parent. Instead, the string's worth,
  14692. based on its rank in the population as a whole, biases the probability of that
  14693. string being selected to parent the next generation. If a string ranks as the
  14694. 25th best out of 100 strings, then it has a 75% chance of becoming a parent.
  14695. A GA's method of selecting parents is very important; it can significantly
  14696. impact the efficiency of the search. Among the many types of selection
  14697. functions, the two most widely used techniques are proportional selection and
  14698. linear rank selection. The knapsack example uses linear rank selection, first
  14699. because it is easy to implement (see CGAPopulation::GetParent, Listing 5).
  14700. More important, I suspect that linear rank selection is inherently better
  14701. behaved than proportional selection, because proportional selection has
  14702. required many fixes to its original design over the years.
  14703. Linear rank selection simply calculates the fitness of a string and then ranks
  14704. it in the entire population. This process involves two random operations.
  14705. First, GetParent randomly selects a candidate string from the population:
  14706. . . .
  14707. Selection=
  14708. Rand0UpTo1();
  14709. . . .
  14710. Next, GetParent determines if the candidate string will parent an offspring,
  14711. by performing a weighted probability (via function Flip, Listing 7) based on
  14712. the string's rank. If the string does not qualify as a parent, then GetParent
  14713. repeats the cycle and randomly selects another string from the population.
  14714.  
  14715.  
  14716.  
  14717. Procreation
  14718.  
  14719.  
  14720. Once two parents have been selected, the GA combines them to create the next
  14721. generation of strings. The GA creates two offspring by combining fragments
  14722. from each of the two parents. The knapsack example uses uniform crossover to
  14723. cut up fragments from the parents (see function Crossover, Listing 3). The
  14724. positions where strings are cut into fragments are called the crossover
  14725. points. Crossover chooses these points at random; uniform crossover means that
  14726. every point has an equal chance of being a crossover point.
  14727. Crossover selection occurs via a crossover mask. Figure 4 illustrates the use
  14728. of a crossover mask. The first child will receive the first parent's character
  14729. if the bit is 1 and the second parent's character if the bit is 0. The second
  14730. child works in reverse.
  14731. Uniform crossover implies many crossover points with an even probability
  14732. distribution across the entire length of each parent string. The fragments
  14733. from the first parent combined with their complementary members from the
  14734. second parent creates two new strings.
  14735.  
  14736.  
  14737. Mutation
  14738.  
  14739.  
  14740. Sometimes the children undergo mutation. The knapsack example uses an
  14741. interesting mutation operator (see CGAChromosome::Mutate, Listing 3). Rather
  14742. than a fixed mutation probability, Mutate uses a probability that changes
  14743. based on the makeup of the population. Mutate compares the two parents of the
  14744. child; greater similarity between parents increases the probability that a
  14745. mutation will occur in the child. The reason for using a variable mutation
  14746. probability is to reduce the chance of premature convergence. This condition
  14747. occurs when the population rushes to a mediocre solution and then simply runs
  14748. out of steam. There is little diversity left and the search becomes a random
  14749. walk among the average. This is similar to a biological species becoming so
  14750. inbred that it is no longer viable. To reduce premature convergence the
  14751. mutation operator kicks in when the population shows little diversity and adds
  14752. new variety by introducing random mutation.
  14753.  
  14754.  
  14755. Finding the Answer
  14756.  
  14757.  
  14758. The two children now can replace two older strings from the population. This
  14759. occurs in function CGAPopulation::ReplaceChromosome (Listing 5). As it does
  14760. with parent selection, the GA chooses these older strings with a random bias.
  14761. In this case, however, the worst string will have the greatest chance of being
  14762. removed. After the insertion of new strings, the population is then ranked
  14763. again. The program stops when any string solves the problem.
  14764. This raises a question: if the best solution is unknown, how can the program
  14765. determine if a it has found the best answer, or at least, one of the better
  14766. answers? One approach would be to solve for a fixed solution that meets some
  14767. predetermined minimally acceptable value. A second approach would be to run
  14768. the program until its rate of finding better answers drops off or the rate of
  14769. improvement of those answers flattens out.
  14770. The knapsack example ran until it found the known answer, which is 24. It
  14771. took, on average, about 160 generations to find the solution -- about 350 out
  14772. of 16,000 possibilities, or 2% of the search space. Indeed, this problem is
  14773. small enough to solve with traditional methods, but by watching how the code
  14774. operates in detail you can get a good idea of how GAs work. The example is
  14775. quite small and expandable. You can try it on different problems simply by
  14776. creating a new ItemDesc structure and the related CalcFitness function. All
  14777. the I/O is confined to the PrintPop function (Listing 1), so you could easily
  14778. drop the example into a larger program.
  14779.  
  14780.  
  14781. Conclusion
  14782.  
  14783.  
  14784. Genetic algorithms balance exploitation with exploration. The crossover and
  14785. mutation operators control exploration while the selection and fitness
  14786. functions control exploitation. Increasing exploitation decreases exploration.
  14787. Mutation increases the ability to explore new areas of the search space but it
  14788. also disrupts the exploitation of the previous generations by changing them.
  14789. Genetic algorithms represent a new, innovative approach to search algorithms.
  14790. Unlike most traditional algorithms, GAs are not deterministic, rather they
  14791. exploit the power of probabilistic operations. By definition and design they
  14792. are adaptive. Survival of the fittest governs the progress of the search, and
  14793. with the possibility of mutations, GAs may explore completely unexpected
  14794. avenues. GAs exhibit a chaotic search behavior very similar to how humans
  14795. conduct searches -- part analytical, part intuition, and part luck.
  14796.  
  14797.  
  14798. Bibliography
  14799.  
  14800.  
  14801. [1] Sedgewick, Robert. Algorithms, 2nd ed. Addison-Wesley, 1988.
  14802. [2] Holland, H. Adaptation in Natural and Artificial Systems. The University
  14803. of Michigan Press, 1975.
  14804. [3] Schaffer, David. Proceedings of the Third International Conference on
  14805. Genetic Algorithms. Morgan Kaufmann, 1989.
  14806. [4] Goldberg, David E. Genetic Algorithms in Search, Optimization, and Machine
  14807. Learning. Addison-Wesley, 1989.
  14808. [5] Levy, Steven. Artificial Life: the Quest for a New Creation. Pantheon
  14809. Books, 1992.
  14810. [6] Davis, Lawrence. Research Notes in Artificial Intelligence, Genetic
  14811. Algorithms, and Simulated Annealing. Morgan Kaufmann, 1987.
  14812. Figure 1 Pseudo code of the genetic algorithm
  14813. Create an initial population of strings.
  14814. Calculate the fitness of each string.
  14815. WHILE an acceptable solution is not found.
  14816.  Select parents for the next generation.
  14817.  Combine the parents to create new offspring.
  14818.  Calculate the fitness of each offspring.
  14819. END WHILE.
  14820. Figure 2 The coding scheme for the knapsack example
  14821. A B B C C D D D D E E E E E Label
  14822. 9 8 8 7 7 4 4 4 4 3 3 3 3 3 Weight
  14823. 13 11 11 10 10 5 5 5 5 4 4 4 4 4 Value
  14824.  
  14825. Strings Wt Value
  14826.  
  14827. 1 0 1 1 0 0 0 0 1 1 1 0 0 0 34 47
  14828. 0 0 0 0 0 1 1 0 0 1 1 0 1 0 17 22
  14829. 1 0 0 0 0 0 0 0 0 0 0 0 1 0 12 17
  14830. Figure 3 Initializing a population of ten strings
  14831.  
  14832. Strings 1s Count Prob.
  14833.  
  14834. U 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 10%
  14835. W 0 0 1 0 0 0 1 0 0 0 0 0 0 0 2 20%
  14836. X 0 1 1 1 0 0 0 0 0 1 1 0 0 1 6 30%
  14837. Y 0 0 1 1 0 1 1 0 0 1 1 0 0 0 6 40%
  14838. X 1 1 0 1 0 1 0 1 1 0 1 1 0 0 8 50%
  14839.  
  14840. String's complement
  14841.  
  14842. ~U 0 1 1 1 1 1 1 1 1 1 1 1 1 1
  14843. ~W 1 1 0 1 1 1 0 1 1 1 1 1 1 1
  14844. ~X 1 0 0 0 1 1 1 1 1 0 0 1 1 0
  14845. ~Y 1 1 0 0 1 0 0 1 1 0 0 1 1 1
  14846. ~X 0 0 1 0 1 0 1 0 0 1 0 0 1 1
  14847.  
  14848. Figure 4 The uniform crossover operator
  14849. Parent 1 1 1 0 0 0 1 0 0 0 1 1 1 0
  14850. Parent 2 1 0 1 1 1 1 1 1 0 0 1 1 0
  14851. Mask 1 0 0 1 1 0 1 0 0 0 0 1 1
  14852. Child 1 1 0 1 0 0 1 0 1 O 0 1 1 O
  14853. Child 2 1 1 0 1 1 1 1 0 0 1 1 1 0
  14854. Table 1 Description of the knapsack items
  14855. Label A B C D E
  14856. --------------------------
  14857. Weight 9 8 7 4 3
  14858. Value 13 11 10 5 4
  14859. Quantity 1 2 2 4 5
  14860. Table 2 The results of the fitness function using a moderate penalty
  14861. Weight 34 17 12
  14862. Value 47 22 17
  14863. Fitness -4 22 17
  14864.  
  14865. Listing 1 Demo program and global functions to solve knapsack problem
  14866. #include <stdlib.h>
  14867. #include <stdio.h>
  14868. #include "pop.h"
  14869.  
  14870. static enum Bool {FALSE, TRUE};
  14871.  
  14872. // MAXWEIGHT defines the constraint of the knapsack
  14873. // example and ItemDesc is simply the coding scheme.
  14874.  
  14875. static const int MAXWEIGHT = 17;
  14876. static const struct
  14877. {
  14878. int Weight;
  14879. int Value;
  14880. } ItemDesc[] = {
  14881. {3, 4}, {3, 4}, {3, 4}, {3, 4}, {3, 4},
  14882. {4, 5}, {4, 5}, {4, 5}, {4, 5},
  14883. {7, 10}, {7, 10}, {8, 11}, {8, 11}, {9, 13}};
  14884.  
  14885. void CalcWeightAndValue(CGAChromosome *Chromosome,
  14886. int& Weight, int& Value);
  14887. Bool FoundSolution(CGAPopulation& Pop);
  14888. void PrintPop(CGAPopulation& Pop);
  14889.  
  14890. // Main creates the population of chromosomes and
  14891.  
  14892. // continues creating new generations until a solution
  14893. // is found.
  14894.  
  14895. int main(void)
  14896. {
  14897. const float MaxMutationRate = 0.2;
  14898. const int PopulationSize = 30;
  14899.  
  14900. CGAPopulation Pop(PopulationSize, sizeof(ItemDesc) /
  14901. sizeof(ItemDesc[0]),
  14902. MaxMutationRate);
  14903.  
  14904. while (!FoundSolution(Pop)) {
  14905. Pop.CreateNextGeneration();
  14906. PrintPop(Pop);
  14907. }
  14908. return EXIT_SUCCESS;
  14909. }
  14910.  
  14911. // Print information and statistics about population.
  14912.  
  14913. void PrintPop(
  14914. CGAPopulation& Pop)
  14915. {
  14916. float TotalFitness = 0;
  14917.  
  14918. printf("Idx Fit Val Wt Chromosome\n");
  14919. for (size_t ChromIdx = 0;
  14920. ChromIdx < Pop.GetPopulationSize();
  14921. ChromIdx++) {
  14922.  
  14923. int Weight;
  14924. int Value;
  14925. CGAChromosome *Chromosome =
  14926. Pop.GetChromosome(ChromIdx);
  14927.  
  14928. TotalFitness += Chromosome->GetFitness();
  14929. CalcWeightAndValue(Chromosome, Weight, Value);
  14930. printf("%3u %4.0f %3d %3d ",
  14931. ChromIdx, Chromosome->GetFitness(),
  14932. Value, Weight);
  14933. for (size_t BitIdx = 0;
  14934. BitIdx < Chromosome->GetLength();
  14935. BitIdx++) {
  14936. printf("%1u", (*Chromosome)[BitIdx]);
  14937. }
  14938. printf("\n");
  14939. }
  14940. printf(
  14941. "Gen, Best, Avg, Worst: %4d, %6.2f, %6.2f, %6.2f\n",
  14942. Pop.GetGeneration(), Pop.GetBestFitness(),
  14943. TotalFitness / ChromIdx,
  14944. Pop.GetChromosome(ChromIdx - 1)->GetFitness());
  14945. getchar();
  14946. }
  14947.  
  14948. // Check if a solution has been found. By definition
  14949. // it must have a value of ANSWER (24) and not exceed
  14950. // MAXWEIGHT (17). Since the fittest chromosome could
  14951.  
  14952. // violate the weight constraint FoundSolution must
  14953. // search through the population of chromosomes.
  14954.  
  14955. Bool FoundSolution(
  14956. CGAPopulation& Pop)
  14957. {
  14958. const int ANSWER = 24;
  14959. int Weight;
  14960. int Value;
  14961.  
  14962. for (size_t ChromIdx = 0;
  14963. ChromIdx < Pop.GetPopulationSize();
  14964. ChromIdx++) {
  14965. CalcWeightAndValue(Pop.GetChromosome(ChromIdx),
  14966. Weight, Value);
  14967. if (Weight <= MAXWEIGHT && Value == ANSWER) {
  14968. return TRUE;
  14969. }
  14970. }
  14971. return FALSE;
  14972. }
  14973.  
  14974. // Calculate the fitness of each chromosome by adding
  14975. // its weight to its value then subtracting a PENALITY
  14976. // for the excess weight.
  14977.  
  14978. float CalcFitness(
  14979. CGAChromosome *Chromosome)
  14980. {
  14981. const float PENALITY = 3.0;
  14982. int Weight;
  14983. int Value;
  14984.  
  14985. CalcWeightAndValue(Chromosome, Weight, Value);
  14986. if (Weight > MAXWEIGHT) {
  14987. return Value - PENALITY * (Weight - MAXWEIGHT);
  14988. } else {
  14989. return Value;
  14990. }
  14991. }
  14992.  
  14993. // Calculate the weight and value of the chromosome by
  14994. // accumulating the weight and value of each item
  14995. // whose bit in the chromosome is set true.
  14996.  
  14997. void CalcWeightAndValue(
  14998. CGAChromosome *Chromosome,
  14999. int& Weight,
  15000. int& Value)
  15001. {
  15002. Weight = 0;
  15003. Value = 0;
  15004. for (size_t Idx = 0; Idx < Chromosome->GetLength();
  15005. Idx++) {
  15006. if ((*Chromosome)[Idx]) {
  15007. Weight += ItemDesc[Idx].Weight;
  15008. Value += ItemDesc[Idx].Value;
  15009. }
  15010. }
  15011.  
  15012. }
  15013. /* End of File */
  15014.  
  15015.  
  15016. Listing 2 Definition of class CGAChromosome and short member functions
  15017. #include "random.h"
  15018.  
  15019. #ifndef _INC_CHROM_____LINEEND____
  15020. #define _INC_CHROM_____LINEEND____
  15021.  
  15022. class CGAChromosome
  15023. {
  15024. public:
  15025. static void InitializeChromosomeClass(
  15026. size_t Length);
  15027. ~CGAChromosome();
  15028. CGAChromosome(float Prob = 0.0);
  15029.  
  15030. CGAChromosome* Compliment() const;
  15031. void Mutate(float Prob);
  15032. inline size_t GetLength() const;
  15033. inline float GetFitness() const;
  15034. friend void Crossover(CGAChromosome* Parent1,
  15035. CGAChromosome* Parent2,
  15036. CGAChromosome*& Child1,
  15037. CGAChromosome*& Child2,
  15038. float Prob);
  15039. friend float CalcSimilarityRatio(
  15040. CGAChromosome* Chrom1,
  15041. CGAChromosome* Chrom2);
  15042. inline GABool& operator[](size_t Idx);
  15043.  
  15044. private:
  15045. static void InitializeCrossoverMask(float Prob);
  15046.  
  15047. static GABool* m_CrossoverMask;
  15048. static size_t m_Count;
  15049. static size_t m_Length;
  15050. GABool* m_Data;
  15051. float m_Fitness;
  15052. };
  15053.  
  15054. size_t CGAChromosome::GetLength() const
  15055. {
  15056. return m_Length;
  15057. }
  15058.  
  15059. float CGAChromosome::GetFitness() const
  15060. {
  15061. return m_Fitness;
  15062. }
  15063.  
  15064. GABool& CGAChromosome::operator[](size_t Idx)
  15065. {
  15066. return m_Data[Idx];
  15067. }
  15068.  
  15069. float CalcFitness(CGAChromosome* Chromosome);
  15070.  
  15071.  
  15072. #endif
  15073. /* End of File */
  15074.  
  15075.  
  15076. Listing 3 More member functions of class CGAChromosome
  15077. #include <stdio.h>
  15078. #include <stdlib.h>
  15079. #include <assert.h>
  15080.  
  15081. #include "chrom.h"
  15082.  
  15083. size_t CGAChromosome::m_Count = 0;
  15084. size_t CGAChromosome::m_Length = 0;
  15085. GABool* CGAChromosome::m_CrossoverMask = 0;
  15086.  
  15087. // Default constructor and destructor.
  15088. // Create the chromosome with bits turned on at the
  15089. // given probability.
  15090.  
  15091. CGAChromosome::CGAChromosome(
  15092. float Prob)
  15093. {
  15094. assert(m_Length);
  15095. m_Fitness = 0;
  15096. m_Data = new GABool[m_Length];
  15097. assert(m_Data);
  15098. m_Count++;
  15099. for (size_t Idx = 0; Idx < m_Length; Idx++) {
  15100. m_Data[Idx] = Flip(Prob);
  15101. }
  15102. m_Fitness = CalcFitness(this);
  15103. }
  15104.  
  15105. CGAChromosome::~CGAChromosome(void)
  15106. {
  15107. delete[] m_Data;
  15108. if (--m_Count == 0) {
  15109. delete[] m_CrossoverMask;
  15110. m_Length = 0;
  15111. m_CrossoverMask = 0;
  15112. }
  15113. }
  15114.  
  15115. // Mutate a single chromosome gene selected at
  15116. // random for a given probability.
  15117.  
  15118. void CGAChromosome::Mutate(
  15119. float Prob)
  15120. {
  15121. for (size_t Idx = 0; Idx < m_Length; Idx++) {
  15122. if (Flip(Prob)) {
  15123. m_Data[Idx] = !m_Data[Idx];
  15124. }
  15125. }
  15126. }
  15127.  
  15128. // Create two new offspring using a uniform crossover
  15129. // operator created with the given probability.
  15130.  
  15131.  
  15132. void Crossover(
  15133. CGAChromosome* Parent1,
  15134. CGAChromosome* Parent2,
  15135. CGAChromosome*& Child1,
  15136. CGAChromosome*& Child2,
  15137. float Prob)
  15138. {
  15139. CGAChromosome::InitializeCrossoverMask(0.5);
  15140. Child1 = new CGAChromosome();
  15141. Child2 = new CGAChromosome();
  15142. assert(Child1 && Child2);
  15143.  
  15144. for (size_t Idx = 0; Idx < CGAChromosome::m_Length;
  15145. Idx++) {
  15146. if (CGAChromosome::m_CrossoverMask[Idx]) {
  15147. Child1->m_Data[Idx] = Parent1->m_Data[Idx];
  15148. Child2->m_Data[Idx] = Parent2->m_Data[Idx];
  15149. } else {
  15150. Child1->m_Data[Idx] = Parent2->m_Data[Idx];
  15151. Child2->m_Data[Idx] = Parent1->m_Data[Idx];
  15152. }
  15153. }
  15154. Child1->Mutate(Prob);
  15155. Child2->Mutate(Prob);
  15156. Child1->m_Fitness = CalcFitness(Child1);
  15157. Child2->m_Fitness = CalcFitness(Child2);
  15158. }
  15159.  
  15160. // Calculate the difference between two chromosomes
  15161.  
  15162. float CalcSimilarityRatio(
  15163. CGAChromosome* Chrom1,
  15164. CGAChromosome* Chrom2)
  15165. {
  15166. for (size_t Idx = 0, MatchCount = 0;
  15167. Idx < CGAChromosome::m_Length; Idx++) {
  15168. if (Chrom1->m_Data[Idx] == Chrom2->m_Data[Idx]) {
  15169. MatchCount++;
  15170. }
  15171. }
  15172. return (float)MatchCount /
  15173. (float)CGAChromosome::m_Length;
  15174. }
  15175.  
  15176. // Set the new chromosome to the opposite encoding
  15177. // of this chromosome.
  15178.  
  15179. CGAChromosome* CGAChromosome::complement(void) const
  15180. {
  15181. CGAChromosome* Chromosome = new CGAChromosome;
  15182. for (size_t Idx = 0; Idx < m_Length; Idx++) {
  15183. Chromosome->m_Data[Idx] = !m_Data[Idx];
  15184. }
  15185. Chromosome->m_Fitness = CalcFitness(Chromosome);
  15186. return Chromosome;
  15187. }
  15188.  
  15189. // Setup the crossover mask for creating the next
  15190. // offspring.
  15191.  
  15192.  
  15193. void CGAChromosome::InitializeCrossoverMask(
  15194. float Prob)
  15195. {
  15196. assert(m_Length && m_CrossoverMask);
  15197. for (size_t Idx = 0; Idx < m_Length; Idx++) {
  15198. m_CrossoverMask[Idx] = Flip(Prob);
  15199. }
  15200. }
  15201.  
  15202. // Setup the chromosomes' length and allocate memory
  15203. // for the crossover mask.
  15204.  
  15205. void CGAChromosome::InitializeChromosomeClass(
  15206. size_t Length)
  15207. {
  15208. assert(Length);
  15209. m_Length = Length;
  15210. if (m_Count) != 0) {
  15211. delete [] m_CrossoverMask;
  15212. }
  15213. m_CrossoverMask = new GaBool[m_Length];
  15214. assert(m_CrossoverMask);
  15215. }
  15216. /* End of File */
  15217.  
  15218.  
  15219. Listing 4 Definition of class CGAPopulation and short member functions
  15220. #include "random.h"
  15221. #include "chrom.h"
  15222.  
  15223. #ifndef _INC_POP_____LINEEND____
  15224. #define _INC_POP_____LINEEND____
  15225.  
  15226. class CGAPopulation
  15227. {
  15228. public:
  15229. ~CGAPopulation();
  15230. CGAPopulation(size_t Size, size_t Length,
  15231. float MaxMutationRate);
  15232.  
  15233. void CreateNextGeneration(void);
  15234. inline float GetBestFitness(void);
  15235. inline size_t GetGeneration(void);
  15236. inline size_t GetPopulationSize(void);
  15237. inline CGAChromosome* GetChromosome(size_t Idx);
  15238.  
  15239. private:
  15240. CGAChromosome* GetParent();
  15241.  
  15242. void ReplaceChromosome(CGAChromosome* Chromosome);
  15243. void Merge(CGAChromosome* NewChromosome);
  15244.  
  15245. size_t m_MaxSize;
  15246. size_t m_CurrentSize;
  15247. CGAChromosome** m_Data;
  15248. size_t m_Generation;
  15249. float m_MaxMutationRate;
  15250. };
  15251.  
  15252.  
  15253. float CGAPopulation::GetBestFitness(void) {
  15254. return m_Data[0]->GetFitness();
  15255. }
  15256.  
  15257. size_t CGAPopulation::GetGeneration(void) {
  15258. return m_Generation;
  15259. }
  15260. size_t CGAPopulation::GetPopulationSize(void) {
  15261. return m_CurrentSize;
  15262. }
  15263. CGAChromosome* CGAPopulation::GetChromosome(
  15264. size_t Idx)
  15265. {
  15266. return m_Data[Idx];
  15267. }
  15268. #endif
  15269. /* End of File */
  15270.  
  15271.  
  15272. Listing 5 More member functions of class CGAPopulation
  15273. #include <stdlib.h>
  15274. #include <stdio.h>
  15275. #include <float.h>
  15276. #include <assert.h>
  15277. #include <string.h>
  15278.  
  15279. #include "pop.h"
  15280. #include "random. h"
  15281.  
  15282. // Constructor. It creates a population of chromosomes
  15283. // where the probability that each bit is true
  15284. // increases with each creation, then it creates a
  15285. // complement of each of the created chromosomes.
  15286.  
  15287. CGAPopulation::CGAPopulation(
  15288. unsigned int Size,
  15289. unsigned int Length,
  15290. float MaxMutationRate)
  15291. {
  15292. assert(Size && Length);
  15293. m_MaxSize = Size;
  15294. m_CurrentSize = 0;
  15295. m_Generation = 0;
  15296. m_MaxMutationRate = MaxMutationRate;
  15297. m_Data = new CGAChromosome*[m_MaxSize];
  15298. CGAChromosome::InitializeChromosomeClass(Length);
  15299.  
  15300. for (unsigned int idx = 1; idx <= m_MaxSize / 2;
  15301. idx++) {
  15302. CGAChromosome* Temp =
  15303. new CGAChromosome(idx / (float)m_MaxSize);
  15304. CGAChromosome* CompTemp = Temp->complement();
  15305. Merge(Temp);
  15306. Merge(CompTemp);
  15307. }
  15308. }
  15309.  
  15310. // Default destructor
  15311.  
  15312.  
  15313. CGAPopulation::~CGAPopulation()
  15314. {
  15315. for (unsigned int idx = 0; idx < m_MaxSize; idx++) {
  15316. delete m_Data[idx];
  15317. }
  15318. delete m_Data;
  15319. }
  15320.  
  15321. // Merge sort the new chromosome, assume that there no
  15322. // holes in the array.
  15323.  
  15324. void CGAPopulation::Merge(
  15325. CGAChromosome* NewChromosome)
  15326. {
  15327. assert(m_CurrentSize < m_MaxSize);
  15328. for (unsigned int idx = 0; idx < m_CurrentSize;
  15329. id++) {
  15330. if (NewChromosome->GetFitness() >
  15331. m_Data[idx]->GetFitness()) {
  15332. break;
  15333. }
  15334. }
  15335. memmove(&m_Data[idx + 1], &m_Data[idx],
  15336. sizeof(m_Data) * (m_CurrentSize - idx));
  15337. m_Data[idx] = NewChromosome;
  15338. m_CurrentSize++;
  15339. }
  15340.  
  15341. // These functions randomly select a chromosome from
  15342. // the ranked array, where the probability of selection
  15343. // is related to the individual's position in the
  15344. // array. ReplaceChromosome calculates an individual's
  15345. // probability for replacement by Rank(X) / Total.
  15346. // GetParent calculates an individual's probability for
  15347. // parenthood by (Total - Rank(X)) / Total).
  15348.  
  15349. CGAChromosome* CGAPopulation::GetParent()
  15350. {
  15351. float Selection;
  15352.  
  15353. do {
  15354. Selection = Rand0UpTo1();
  15355. } while (Flip(Selection));
  15356. return m_Data[(int)(Selection * m_MaxSize)];
  15357. }
  15358.  
  15359. // Replace a poor chromosome with the new one.
  15360.  
  15361. void CGAPopulation::ReplaceChromosome(
  15362. CGAChromosome* NewChromosome)
  15363. {
  15364. float Selection;
  15365. do {
  15366. Selection = Rand0UpTo1();
  15367. } while (Flip(Selection));
  15368.  
  15369. unsigned int idx = m_MaxSize -
  15370. (int)(Selection * m_MaxSize) - 1;
  15371.  
  15372. delete m_Data[idx];
  15373. memmove(&m_Data[idx], &m_Data[idx + 1],
  15374. sizeof(m_Data) * (m_CurrentSize - idx - 1));
  15375. m_CurrentSize--;
  15376. Merge(NewChromosome);
  15377. }
  15378.  
  15379. // Create two offspring and replace two members of the
  15380. // chromosome array.
  15381.  
  15382. void CGAPopulation::CreateNextGeneration(void)
  15383. {
  15384. CGAChromosome *Parent1, *Parent2, *Child1, *Child2;
  15385.  
  15386. Parent1 = GetParent();
  15387. Parent2 = GetParent();
  15388. Crossover(Parent1, Parent2, Child1, Child2,
  15389. CalcSimilarityRatio(Parentl, Parent2) *
  15390. m_MaxMutationRate);
  15391. ReplaceChromosome(Child1);
  15392. ReplaceChromosome(Child2);
  15393. m_Generation++;
  15394. }
  15395. /* End of File */
  15396.  
  15397.  
  15398. Listing 6 random.h -- header file for probability functions
  15399. z#ifndef _INC_GLOBAL_____LINEEND____
  15400. #define _INC_GLOBAL_____LINEEND____
  15401.  
  15402. typedef unsigned char GABool;
  15403.  
  15404. float Rand0UpTo1(void);
  15405. float Rand0To1(void);
  15406. GABool Flip(float Prob);
  15407.  
  15408. #endif
  15409.  
  15410. /* End of File */
  15411.  
  15412.  
  15413. Listing 7 Probability functions
  15414. #include <stdlib.h>
  15415.  
  15416. #include "random.h"
  15417.  
  15418. // Return a pseudo-random number from 0.0 upto,
  15419. // but not including, 1.0.
  15420.  
  15421. float Rand0UpTo1(void)
  15422. {
  15423. return rand() / (float)(RAND_MAX + 1.0);
  15424. }
  15425.  
  15426. // Return a pseudo-random number between 0.0 and
  15427. // 1.0 inclusive.
  15428.  
  15429. float Rand0To1(void)
  15430. {
  15431.  
  15432. return rand() / (float)RAND_MAX;
  15433. }
  15434.  
  15435. // Return TRUE at the specified probability.
  15436.  
  15437. GABool Flip(float Prob)
  15438. {
  15439. return Rand0To1() <= Prob;
  15440. }
  15441.  
  15442. /* End of File */
  15443.  
  15444.  
  15445.  
  15446.  
  15447.  
  15448.  
  15449.  
  15450.  
  15451.  
  15452.  
  15453.  
  15454.  
  15455.  
  15456.  
  15457.  
  15458.  
  15459.  
  15460.  
  15461.  
  15462.  
  15463.  
  15464.  
  15465.  
  15466.  
  15467.  
  15468.  
  15469.  
  15470.  
  15471.  
  15472.  
  15473.  
  15474.  
  15475.  
  15476.  
  15477.  
  15478.  
  15479.  
  15480.  
  15481.  
  15482.  
  15483.  
  15484.  
  15485.  
  15486.  
  15487.  
  15488.  
  15489.  
  15490.  
  15491.  
  15492.  
  15493.  
  15494.  
  15495. Standard C/C++
  15496.  
  15497.  
  15498. The Header <sstream>
  15499.  
  15500.  
  15501.  
  15502.  
  15503. P.J. Plauger
  15504.  
  15505.  
  15506. P.J. Plauger is senior editor of C/C++ Users Journal. He is convener of the
  15507. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  15508. latest books are The Draft Standard C++ Library, and Programming on Purpose
  15509. (three volumes), all published by Prentice-Hall. You can reach him at
  15510. pjp@lauger.com.
  15511.  
  15512.  
  15513.  
  15514.  
  15515. Introduction
  15516.  
  15517.  
  15518. The header <sstream> is a variation on the header (strstream>, which I have
  15519. described in the previous two installments of this column. (See "Standard
  15520. C/C++: The Header <strstream>," CUJ, January 1995, and "Standard C/C++:
  15521. Implementing <strstream>," CUJ, February 1995.) It is designed to work more
  15522. closely with class string. A string object controls an in-memory character
  15523. sequence, supporting operations on the sequence such as assignment,
  15524. concatenation, comparison, and searching.
  15525. Like <strstream>, <sstream) defines three classes that cooperate to help you
  15526. read and write character sequences stored in memory:
  15527.  stringbuf, derived from streambuf to mediate access to an in-memory character
  15528. sequence and grow it on demand (yes, the name should be stringstreambuf for
  15529. greater uniformity)
  15530. istringstream, derived from istream to construct a stringbuf object with input
  15531. and/or output streams and to assist in extracting from the stream
  15532. ostringstream, derived from ostream to construct a stringbuf object with input
  15533. and/or output streams and to assist in inserting into the stream
  15534. If these classes sound suspiciously like the classes defined in the header
  15535. <strstream), that is hardly an accident. A stringbuf object supports much the
  15536. same control over input and output streams as does a strstreambuf object.
  15537. There are just a few added capabilities:
  15538. You can initialize the character sequence controlled by a stringbuf from the
  15539. character sequence controlled by a string, when the stringbuf is constructed
  15540. or repeatedly thereafter.
  15541. You can initialize the character sequence controlled by a newly constructed
  15542. string object from the character sequence controlled by a stringbuf.
  15543. You can specify independently, when a stringbuf is constructed, whether the
  15544. input stream is readable or the output stream is writable.
  15545. Unlike a strstreambuf object, however, a stringbuf object does not let you
  15546. muck with a character sequence that is also controlled by a string object. It
  15547. simply facilitates copying between two representations private to each object.
  15548. The classes istringstream and ostringstream behave much like istrstream and
  15549. ostrstream. They construct a stringbuf object for you. They also provide
  15550. member functions that access the special features of a stringbuf object. In
  15551. this case, that mostly involves copying to and from string objects, as
  15552. described above.
  15553. A few subtle differences exist between class stringbuf and class strstreambuf.
  15554. If an object of the latter class defines an output sequence, it always defines
  15555. an input sequence as well. But that is not always true for an object of class
  15556. stringbuf. Otherwise, the two kinds of stream buffers are more alike than
  15557. different.
  15558.  
  15559.  
  15560. Recent Changes
  15561.  
  15562.  
  15563. As I emphasized last month, the code I present here is based on the draft C++
  15564. Standard as of March 1994. Despite the many changes that have occurred in the
  15565. past year, the functionality of the classes defined in <sstream) is
  15566. essentially unchanged -- at least for the char-based streams we all know and
  15567. love. But many headers have now been "templatized." In particular, iostreams
  15568. operations are now mostly defined for an arbitrary "character" type T.
  15569. To reproduce the existing functionality, the library instantiates these
  15570. general templates for T defined as type char. The net result is much the same,
  15571. but the machinery -- and the notation -- is now far more elaborate. I prefer
  15572. to show the behavior of iostreams for the more familiar char-based streams.
  15573. The library also instantiates these templates for T defined as type wchar_t.
  15574. If you want to manipulate wide-character strings, that can be invaluable, but
  15575. such usage is still relatively rare. Even more rare is the use of iostreams
  15576. based on arbitrary characters of type T. (As far as I know, such usage is
  15577. nonexistent.) It will be interesting to see how useful such generality will
  15578. prove in future practice.
  15579. I also reported last month that the header <strstream) has been exempted from
  15580. the templatizing treatment. The committee is staking the future on <sstream>,
  15581. which does much the same thing as <strstream) but works with templatized
  15582. strings of arbitrary character types. The older header <strstream> is retained
  15583. mostly in the interest of preserving existing code.
  15584. Finally, I note that the member function name str has already been changed in
  15585. class string since March 1994. It is now data. That cleanup has yet to extend
  15586. to the classes defined in <sstream>, but it might.
  15587.  
  15588.  
  15589. Using <sstream>
  15590.  
  15591.  
  15592. You include the header <sstream> to make use of any of the classes
  15593. istringstream ostringstream or stringbuf. Objects of these classes let you
  15594. read and write in-memory character sequences just as if they were conventional
  15595. files, and copy character sequences. You can choose among three patterns of
  15596. access:
  15597. read only
  15598. write only
  15599. read/write
  15600. I deal with each of these options in turn. For a discussion of
  15601. stream-positioning operations on in-memory character sequences, see the
  15602. January 1995 installment. The issues are essentially the same.
  15603. If all you want to do is read an inmemory character sequence that is
  15604. initialized from a string object, construct an object of class istringstream.
  15605. If you know at construction time what string object s you wish to use, you can
  15606. write:
  15607. istringstream strin(s);
  15608. The resultant stream buffer (pointed at by strin.rdbuf()) does not support
  15609. insertions. You can, however, replace the character sequence completely from
  15610. another string object s2 with the call:
  15611. strin.str(s2);
  15612. The stream position is reset to the beginning of the stream. (And the
  15613. resultant stream buffer still does not support insertions.)
  15614. You can also construct an istringstream object with an empty character
  15615. sequence, using the default constructor. Presumably, you would later supply a
  15616. non-empty character sequence, as in:
  15617.  
  15618. istringstream strin;
  15619. strin.str(s);
  15620. If all you want to do is create an in-memory character sequence to be
  15621. eventually copied to a string object, construct an object of class
  15622. ostringstream to control insertions into it. You can write:
  15623. ostringstream strout;
  15624. then insert into strout just like any other output stream. The character
  15625. sequence can grow dynamically to arbitrary length. (The actual limit is
  15626. usually INT_MAX, defined in <limits.h>, or when a storage allocation request
  15627. fails.) The resultant stream buffer (pointed at by strout. rdbuf()) does not
  15628. support extractions, by the way.
  15629. Your goal in creating a write-only character sequence is to capture the final
  15630. result in a string object, as a rule. Write s = strout.str() to construct a
  15631. string object, initialize it to control a copy of the character sequence, and
  15632. assign it to the string object s. The two character sequences can, of course,
  15633. evolve separately thereafter.
  15634. If you want a null-terminated string, there is no real need to insert a null
  15635. character last. It will not be supplied for you when you call s =
  15636. strout.str(), as above. On the other hand, you must then call s.c_str() to get
  15637. a pointer to the beginning of the character sequence. That call will supply a
  15638. terminating null character.
  15639. If you want to create an in-memory character sequence that you can read as
  15640. well as write, you need two objects to control the input and output streams.
  15641. The classes istringstream and ostringstream are highly symmetric. Thus, you
  15642. have three equally valid ways to do the job. If you don't want to supply an
  15643. initial character sequence, you can write:
  15644. istringstream istr(ios::in ios::out);
  15645. ostream ostr(istr. rdbuf());
  15646. or:
  15647. ostringstream ostr(ios::in ios::out);
  15648. istream istr(ostr. rdbuf());
  15649. or:
  15650. stringbuf sb(ios::in ios::out);
  15651. istream istr(&sb);
  15652. ostream ostr(&sb);
  15653. All approaches cause istr to control the input stream and ostr to control the
  15654. output stream.
  15655. You can also supply an initial character sequence from a string object s in
  15656. each of these three cases:
  15657. istringstream istr(s, ios::in ios::out);
  15658. ostream ostr(istr. rdbuf( ) );
  15659. or:
  15660. ostringstream ostr(s, ios::in ios::out);
  15661. istream istr(ostr. rdbuf() );
  15662. or:
  15663. stringbuf sb(s, ios::in ios::out);
  15664. istream istr(&sb);
  15665. ostream ostr(&sb);
  15666. Note that both the input and output stream positions are initially at the
  15667. beginning of the character sequence. That may not be what you intend. Always
  15668. consider whether you want to alter the output stream position before you do
  15669. anything else with such a read/write stream.
  15670.  
  15671.  
  15672. Implementing <sstream>
  15673.  
  15674.  
  15675. Listing 1 shows the file sstream, which implements the standard header
  15676. <sstream>. It defines the classes stringbuf, istringstream, and ostringstream.
  15677. Note that class stringbuf is based on class strstreambuf, which I described
  15678. last month. The draft C++ Standard says that stringbuf is derived directly
  15679. from streambuf. Such indirect derivation is permitted by the library "front
  15680. matter."
  15681. I chose this implementation, as I hinted last month, because the two derived
  15682. classes are so much alike. I added a bit of logic to class strstreambuf to
  15683. close the gap:
  15684. I added the element _Noread to the type strstreambuf ::_Strmode, to note when
  15685. a stringbuf object does not support extractions.
  15686. The member function strstreambuf::_Init has a default fourth argument, of type
  15687. strstreambuf::_Strmode, to communicate extra mode information from a stringbuf
  15688. constructor.
  15689. The member function strstreambuf::_Tidy is a separate function, even though it
  15690. is called only by the destructor for strstreambuf, It thus can also be called,
  15691. to advantage, by stringbuf::str(const string&).
  15692. I also added to class stringbuf the secret protected member function _Mode. It
  15693. maps constructor arguments of type ios::openmode to their corresponding
  15694. strstreambuf::_Strmode values:
  15695. If ios::in is not set, the function sets_Strmode::_Noread in the return value.
  15696. If ios::out is not set, the function sets _Strmode::_Constant in the return
  15697. value.
  15698. The effect of all this groundwork is to dramatically reduce the amount of new
  15699. code required to implement the classes defined in <sstream>.
  15700. Listing 2 shows the file stringbu. c, which defines the two functions required
  15701. for practically any use of class stringbuf. These are the destructor and the
  15702. member function stringbuf::_Mode. Class stringbuf has only two additional
  15703. member functions not defined inline, the two flavors of str.
  15704. Listing 3 shows the file strbstr0. c, which defines the member function
  15705. stringbuf::str(). If an input stream exists, the complexity lies in
  15706. determining the current extent of the character sequence. The calculation is
  15707. reminiscent of the logic for updating the strstreambuf member object
  15708. _Seekhigh. (See last month.)
  15709. Listing 4 shows the file strbstr1.c, which defines the member function
  15710. stringbuf::str(const string&). It discards any existing character sequence and
  15711. reinitializes the stream buffer to control a copy of the new one. The
  15712. strstreambuf secret member functions really pay off here.
  15713. The remaining source files implement the other two classes defined in
  15714. <sstream>.
  15715. Listing 5 shows the file istrings. c, which defines the destructor for class
  15716. istringstream. And Listing 6 shows the file ostrings. c, which defines the
  15717. destructor for class ostringstream. The header supplies inline definitions for
  15718. all other member functions in these two classes.
  15719.  
  15720.  
  15721. Testing <sstream>
  15722.  
  15723.  
  15724. Listing 7 shows the file tsstream. c. It tests the basic properties of the
  15725. classes defined in <sstream>. It does so in three groups:
  15726. stringbuf objects (t1())
  15727. istringstream objects (t2())
  15728. ostringstream objects (t3())
  15729. The function main() simply performs these groups of tests in the order shown.
  15730. If all goes well, the program prints:
  15731. SUCCESS testing <sstream>
  15732.  
  15733. and takes a normal exit.
  15734. This article is excerpted in part from P.J. Plauger, The Draft Standard C++
  15735. Library, (Englewood Cliffs, N.J.: Prentice-Hall, 1995).
  15736.  
  15737. Listing 1 The header <sstream>
  15738. // sstream standard header
  15739. #ifndef _SSTREAM_____LINEEND____
  15740. #define _SSTREAM_____LINEEND____
  15741. #include <string>
  15742. #include <strstream>
  15743. // class stringbuf
  15744. class stringbuf : public strstreambuf {
  15745. public:
  15746. stringbuf(ios::openmode _W = ios::in ios::out)
  15747. : strstreambuf(0, 0, 0, _Mode(_W)) {}
  15748. stringbuf(const string& _S,
  15749. ios::openmode_W = ios::in ios::out)
  15750. : strstreambuf((char*)_S.c_str( ,_S.length(), 0,
  15751. _Mode(_W)) {}
  15752. virtual ~stringbuf();
  15753. string str() const;
  15754. void str(const string& _S);
  15755. protected:
  15756. _Strstate _Mode(ios::openmode);
  15757. };
  15758. // class istrstream
  15759. class istringstream : public istream {
  15760. public:
  15761. istringstream(openmode _W = in)
  15762. : istream(&_Sb), _Sb(_W) {}
  15763. istringstream(const string&_S, openmode _W = in)
  15764. : istream(&_Sb), _Sb(_S, _W) {}
  15765. virtual ~istringstream();
  15766. stringbuf *rdbuf() const
  15767. {return ((stringbuf *)&_Sb); }
  15768. string str() const
  15769. {return (_Sb.str()); }
  15770. void str(const string& _S)
  15771. {_Sb.str(_S); }
  15772. private:
  15773. stringbuf _Sb;
  15774. };
  15775. // class ostrstream
  15776. class ostringstream: public ostream {
  15777. public:
  15778. ostringstream(openmode_W = out)
  15779. : ostream(&_Sb), _Sb(_W) {}
  15780. ostringstream(const string& _S, openmode _W = out)
  15781. : ostream(&_Sb), _Sb(_S, _W) {}
  15782. virtual ~ostringstream();
  15783. stringbuf *rdbuf() const
  15784. {return ((stringbuf *)&_Sb); }
  15785. string str() const
  15786. {return (_Sb.str());)
  15787. void str(const string& _S)
  15788. {_Sb.str(_S); }
  15789. private:
  15790. stringbuf _Sb;
  15791. };
  15792. #endif /* _SSTREAM_ */
  15793.  
  15794.  
  15795.  
  15796. Listing 2 The file stringbu.c
  15797. // stringbuf -- stringbuf basic members
  15798. #include <sstream>
  15799.  
  15800. stringbuf::~stringbuf()
  15801. { // destruct a stringbuf
  15802. }
  15803.  
  15804. strstreambuf::_Strstate stringbuf::_Mode(ios::openmode which)
  15805. { // map ios::openmode to _Strstate
  15806. _Strstate mode = _Dynamic;
  15807. if (!(which & ios::in))
  15808. mode = _Noread;
  15809. if (!(which & ios::out))
  15810. mode = _Constant;
  15811. return (mode);
  15812. }
  15813.  
  15814.  
  15815. Listing 3 The file strbstr0.c
  15816. // strbstr0 -- stringbuf::str()
  15817. #include <sstream>
  15818.  
  15819. string stringbuf::str() const
  15820. { // construct string from stringbuf
  15821. if (gptr() != 0)
  15822. return (string(eback(),
  15823. (pptr() == 0 pptr() < egptr() ? egptr(): pptr())
  15824. - eback( ) ) );
  15825. else if (!(_Strmode & _Constant) && pptr() != 0)
  15826. return (string(pbase(), pptr() - pbase()));
  15827. else
  15828. return (string(""));
  15829. }
  15830.  
  15831.  
  15832. Listing 4 The file strbstr1. c
  15833. // strbstr1 -- stringbuf::str(const string&)
  15834. #include <sstream>
  15835.  
  15836. void stringbuf::str(const string& str)
  15837. { // construct stringbuf from string
  15838. _Tidy();
  15839. _Init(str.length(), (char *)str.c_str(), 0, _Strmode);
  15840. }
  15841.  
  15842.  
  15843. Listing 5 The file istrings.c
  15844. // istringstream -- istringstream basic members
  15845. #include <sstream>
  15846.  
  15847. istringstream::~istringstream()
  15848. { // destruct an istringstream
  15849. }
  15850.  
  15851.  
  15852. Listing 6 The file ostrings.c
  15853.  
  15854. // ostringstream -- ostringstream basic members
  15855. #include <sstream>
  15856.  
  15857. ostringstream::~ostringstream()
  15858. { // destruct an ostringstream
  15859. }
  15860.  
  15861.  
  15862. Listing 7 The file tsstream.c
  15863. // test <sstream>
  15864. #include <cassert>
  15865. #include <iostream>
  15866. #include <sstream>
  15867.  
  15868. void t1()
  15869. { // test stringbuf
  15870. string s0("s0"), s1("s1"), s2("s2"), s3("s3");
  15871. stringbuf sb0, sb1(ios::in), sb2(ios::out),
  15872. sb3(ios::in ios::out);
  15873. stringbuf sb10(s0), sb11(s1, ios::in),
  15874. sb12(s2, ios::out),
  15875. sb13(s3, ios::in ios::out);
  15876. ostream outs(&sb0);
  15877. outs << "dynamic stringbuf 0";
  15878. s3 = sb0.str();
  15879. assert(s3 == "dynamic stringbuf 0");
  15880. sb0.str(s0);
  15881. assert(sb0.str() == "s0");
  15882. outs.rdbuf(&sb2);
  15883. outs << "dynamic stringbuf 2";
  15884. assert(sb2.str() == "dynamic stringbuf 2");
  15885. outs.rdbuf(&sb10);
  15886. outs << "x";
  15887. assert(sb10.str() == "x0");
  15888. outs.rdbuf(&sb11);
  15889. outs << "x";
  15890. assert(!outs.good() && sb12.str() == "s1");
  15891. outs.rdbuf(&sb12);
  15892. outs << "x";
  15893. assert(sb12.str() // "x");
  15894. assert(sb12.pubseekoff(2, ios::beg).offset() == 2
  15895. && sb12.str() == "x2");
  15896. }
  15897.  
  15898. void t2()
  15899. { // test istringstream
  15900. string s0("s0"), s1("s1"), s2("s2"), s3("s3");
  15901. istringstream is0, is1(ios::in),
  15902. is2(ios::out), is3(ios::in ios::out);
  15903. istringstream is10(s0), is11(s1, ios::in),
  15904. is12(s2, ios::out),
  15905. is13(s3, ios::in ios::out);
  15906. assert(is10.rdbuf()->str() == "s0")
  15907. assert(is11.str() == "s1");
  15908. is0.str("abc");
  15909. assert(is0.str() == "abc");
  15910. is0 >> s0;
  15911. assert(s0 == "abc");
  15912. }
  15913.  
  15914.  
  15915. void t3()
  15916. { // test ostringstream
  15917. string s0("s0"), s1("s1"), s2("s2"), s3("s3");
  15918. ostringstream os0, osl(ios::in),
  15919. os2(ios::out), os3(ios::in ios::out);
  15920. ostringstream os10(s0), os11(s1, ios::in),
  15921. os12(s2, ios::out),
  15922. os13(s3, ios::in ios::out);
  15923. assert(os10.rdbuf()->str() == "");
  15924. assert(os13.str() == "s3");
  15925. os0.str("abc");
  15926. assert(os0.str() == "");
  15927. assert(os0.rdbuf()->pubseekoff(2, ios::beg).offset()
  15928. == 2 && os0.str() == "ab");
  15929. os0 << "Cde";
  15930. assert(os0.str() == "abCde");
  15931. }
  15932.  
  15933. int main()
  15934. { // test basic workings of stringstream definitions
  15935. t1();
  15936. t2();
  15937. t3();
  15938. cout << "SUCCESS testing <sstream>" << endl;
  15939. return (0);
  15940. }
  15941.  
  15942.  
  15943.  
  15944.  
  15945.  
  15946.  
  15947.  
  15948.  
  15949.  
  15950.  
  15951.  
  15952.  
  15953.  
  15954.  
  15955.  
  15956.  
  15957.  
  15958.  
  15959.  
  15960.  
  15961.  
  15962.  
  15963.  
  15964.  
  15965.  
  15966.  
  15967.  
  15968.  
  15969.  
  15970.  
  15971.  
  15972.  
  15973.  
  15974.  
  15975.  
  15976.  
  15977. Code Capsules
  15978.  
  15979.  
  15980. The Standard C Library, Part 3
  15981.  
  15982.  
  15983.  
  15984.  
  15985. Chuck Allison
  15986.  
  15987.  
  15988. Chuck Allison is a regular columnist with CUJ and a Senior Software Engineer
  15989. in the Information and Communication Systems Department of the Church of Jesus
  15990. Christ of Latter Day Saints in Salt Lake City. He has a B.S. and M.S. in
  15991. mathematics, has been programming since 1975, and has been teaching and
  15992. developing in C since 1984. His current interest is object-oriented technology
  15993. and education. He is a member of X3J16, the ANSI C++ Standards Committee.
  15994. Chuck can be reached on the Internet at 72640.1507@compuserve. com.
  15995.  
  15996.  
  15997. This month I conclude this series on the Standard C Library by exploring the
  15998. headers in Group III (see Table 1 through Table 3).
  15999.  
  16000.  
  16001. Group III: For the "Complete" C Programmer
  16002.  
  16003.  
  16004.  
  16005.  
  16006. <float.h>
  16007.  
  16008.  
  16009. Perhaps nothing varies so widely across different computer environments as
  16010. floating-point number systems. Some platforms require special hardware to make
  16011. native floating-point instructions available. Others provide floating-point
  16012. capability through software, and MS-DOS compilers must support both
  16013. implementations.
  16014. A floating-point number system is a collection of numbers that can be
  16015. expressed in scientific notation, with a fixed number of digits. To be
  16016. precise, it is the finite set of numbers of the form Â±0.d1d2..dp x be, where m
  16017. £ e Â£ M. The parameters b, p, m, and M represent the radix, precision, minimum
  16018. exponent, and maximum exponent, respectively. The header <float.h> provides
  16019. manifest constants for these and other important floating-point parameters
  16020. (see Table 4; also see the sidebar, "Floating-Point Number Systems"). As Table
  16021. 4 illustrates, each implementation must support three potentially separate
  16022. number systems, one each for float, double, and long double numbers.
  16023.  
  16024.  
  16025. <math.h>
  16026.  
  16027.  
  16028. Although C is not used widely in scientific programming, it provides a
  16029. full-featured set of mathematical functions (see Table 6). Most of these
  16030. functions have the standard names used in mathematics, so if you know what
  16031. they are, you know how to use them. A few deserve special mention, however. As
  16032. the program in Listing 4 illustrates, the fmod function computes the remainder
  16033. of dividing its first argument by its second. The answer to fmod(1234.56,
  16034. 90.1234) is 62.9558 because
  16035. 1234.56 - (13 * 90.1234) == 62.9558
  16036. The function modf stores the integer part of its first argument at the address
  16037. given by its second argument, and returns the fractional part. frexp splits a
  16038. floating-point number as given by its first argument into two parts, mantissa
  16039. and base-2 exponent. In Listing 4, frexp computes a normalized fraction w and
  16040. an integer p such that its first argument, y, is equivalent to
  16041. w x 2p
  16042. frexp's inverse, ldexp, returns a floating-point number given mantissa w and
  16043. exponent p.
  16044. The floor of a number is itself if that number is an integer; otherwise the
  16045. floor of a number is the next adjacent integer to the "left" on the number
  16046. line, so the following relationships are valid:
  16047. floor(90.1234) == 90
  16048. floor(-90.1234) == -91
  16049. The ceiling of a number is the next integer to the right, so
  16050. ceil(90.1234) == 91
  16051. ceil(-90.1234) == -90
  16052. The calculator program in Listing 5 illustrates a number of <math.h>
  16053. functions.
  16054.  
  16055.  
  16056. <errno.h>
  16057.  
  16058.  
  16059. <errno.h> implements a simple error reporting facility. It defines a global
  16060. integer, errno, to hold certain error codes that are generated by a number of
  16061. library functions. The C Standard requires vendors to provide only two codes,
  16062. EDOM and ERANGE (see Table 7). EDOM indicates a domain error, which usually
  16063. means that you passed bad arguments to the function. For example, the sqrt
  16064. function in <math. h> complains if you ask for the square root of a negative
  16065. number. Math functions can set errno to ERANGE to indicate a range error,
  16066. which mean that a calculation on valid arguments would result in an arithmetic
  16067. overflow or underflow. Most of the mathematical functions in the standard
  16068. library use errno to report such errors. As the calculator in Listing 5
  16069. illustrates, you should set errno to zero before invoking any function that
  16070. uses this facility, and then check it immediately after the function returns.
  16071. The function perror prints its string argument followed by a colon, followed
  16072. by a string representation of the last error recorded in errno. perror(s) is
  16073. equivalent to the expression:
  16074. printf("%s: %s\n",s,strerror(errno));
  16075. strerror, defined in <string.h>, returns implementation-defined text that
  16076. corresponds to errno.
  16077. The following functions from other standard library headers also set errno
  16078. upon failure: strod, strtol, strtoul, fgetpos, fsetpos, and signal. The C
  16079. Standard allows an implementation to provide error codes of its own, beyond
  16080. EDOM and ERANGE, and to use the errno facility with other functions, but such
  16081. use is of course non-portable.
  16082.  
  16083.  
  16084. <locale.h>
  16085.  
  16086.  
  16087.  
  16088. A locale in Standard C is a collection of preferences for the processing and
  16089. display of information that is sensitive to culture, language, or national
  16090. origin, such as date and monetary formats. The Standard recognizes five
  16091. categories of locale-specific information, named by macros in <locale.h> (see
  16092. Table 8). Each of these categories can be set to a different locale (e.g.,
  16093. "american", or "italian"). For want of a better term, I call the collection of
  16094. settings for all five categories the locale profile.
  16095. Standard C specifies two functions that deal with locales directly:
  16096. struct lconv *localeconv(void);
  16097. char *setlocale(int category, char *locale);
  16098. The members of the lconv structure appear in Listing 6. localeconv returns a
  16099. static lconv object containing current settings for LC_MONETARY and
  16100. LC_NUMERIC, and setlocale changes the locale for the given category to that
  16101. specified in its second argument. You can set all categories to the given
  16102. locale by specifying a category of LC_ALL (see Listing 7). If locale is NULL,
  16103. setlocale returns the current locale string for the category. All
  16104. implementations must support the minimalist "C" locale, and a native locale
  16105. named by the empty string (which may be the same as the "C" locale).
  16106. Unfortunately, few U.S. vendors provide any additional locale support.
  16107.  
  16108.  
  16109. <setjmp.h)
  16110.  
  16111.  
  16112. When you encounter an exceptional condition deep within a set of nested
  16113. function calls, you need a "super goto" that branches to a safe point higher
  16114. up in the function call stack. That's what the setjmp/longjmp mechanism is
  16115. for. You mark that safe return point with setjmp, and branch to it with
  16116. longjmp. Here's the syntax:
  16117. #include <setjmp.h>
  16118.  
  16119. jmp_buf recover;
  16120.  
  16121. main()
  16122. {
  16123.  
  16124. volatile int i = 0;
  16125. for(;;)
  16126. {
  16127. if (setjmp(recover) != 0)
  16128. {
  16129. /* Recover from error in f() */
  16130. }
  16131.  
  16132. /* Get deeply nested... */
  16133. }
  16134. return 0;
  16135. }
  16136.  
  16137. . . .
  16138.  
  16139. void f()
  16140. {
  16141. /* Do some risky stuff */
  16142.  
  16143. if (<things go crazy>)
  16144. longjmp(recover,1);
  16145.  
  16146. /* else carry on */
  16147. }
  16148. A jmp_buf (jump buffer) is an array that holds the system information
  16149. necessary to restore execution at the setjmp point. For obvious reasons, a
  16150. jump buffer must typically be global. When you call setjmp, the system stores
  16151. the calling environment parameters, such as the contents of the stack and
  16152. instruction pointer registers, in recover. setjmp always returns a zero when
  16153. called directly. A call to longjmp restores the calling environment, so
  16154. execution continues back at the setjmp call point, with one difference: it
  16155. appears as if setjmp has returned the second argument from the longjmp call,
  16156. in this case a 1. (One small quirk: if you give longjmp a second argument of
  16157. zero, it returns a 1 anyway. Zero is the only argument with this behavior.)
  16158. Since a longjmp performs an alternate return from a function, it interrupts
  16159. the normal flow of a program, so you should use it only to handle unusual
  16160. conditions.
  16161. When longjmp is called, the function containing the setjmp target must still
  16162. be active (i.e., must not yet have returned to its own caller), otherwise the
  16163. calling environment will no longer be valid. And of course, it is a bad idea
  16164. to have more than one setjmp target with the same jmp_buf variable. Since
  16165. calling environments typically involve registers, and since a compiler is free
  16166. to store automatic variables in registers, you have no idea if an automatic
  16167. object will have the correct value when you return from a longjmp. To get
  16168. around this problem, you should declare automatics in any function containing
  16169. a setjmp with the volatile qualifier, which guarantees that the inner workings
  16170. of longjmp will leave them undisturbed. For a more detailed example of the
  16171. setjmp/longjmp mechanism, see Listing 9 in "Code Capsules, File Processing,
  16172. Part 2," CUJ, June 1993.
  16173.  
  16174.  
  16175. <signal.h>
  16176.  
  16177.  
  16178. A signal occurs when an unusual event interrupts the normal execution of a
  16179. program, such as a divide-by-zero error or when the user presses an attention
  16180. key, such as Control-C or DEL. The header <signal. h> defines six "standard
  16181. signals," shown in Table 9. These signals originated on the PDP architecture
  16182. under UNIX, so some of them may not apply to your environment. An
  16183. implementation may also define other signals, or may ignore signals
  16184. altogether, so signal handling is inherently non-portable.
  16185. Signals come in two flavors: synchronous and asynchronous. A synchronous
  16186. signal is one that your program raises, such as dividing by zero, overflowing
  16187. during a floating-point operation, or issuing a call to the abort function.
  16188. You can also raise a signal explicitly with a call to raise, for example:
  16189. raise(SIGABRT);
  16190. An asynchronous signal occurs as a result of events that your program can't
  16191. foresee, such as a user pressing the attention key.
  16192. The default response to most signals is usually to abort the program, but you
  16193. can either arrange for signals to be ignored or provide your own custom signal
  16194. handlers. The following statement from Listing 8
  16195. signal(SIGINT,SIG_IGN);
  16196. tells the environment to ignore any keyboard interrupt requests (Control-C on
  16197. my machine). The keyboard input process still echoes the ^C token on the
  16198. display, but it does not pass control to the default signal-handling logic
  16199. that terminates the program. The statement
  16200. signal(SIGINT,SIG_DFL)
  16201. restores the default behavior, so you can halt the program from the keyboard.
  16202.  
  16203. For more on signal handling, see Listing 11 - Listing 13 in the Code Capsule
  16204. "Control Structures," in the June 1994 issue of CUJ.
  16205.  
  16206.  
  16207. <stdarg.h>
  16208.  
  16209.  
  16210. This header provides a facility for defining functions with variable-length
  16211. argument lists, like printf's (see Table 10). printf's function prototype in
  16212. your compiler's stdio.h should look something like
  16213. int printf(const char *, ...);
  16214. The ellipsis tells the compiler to allow zero or more arguments of any type to
  16215. follow the first argument in a call to printf. For printf to behave correctly,
  16216. the arguments that follow the format string must match the types of the
  16217. corresponding edit descriptors in the format string. If there are fewer
  16218. arguments than the format string expects, the result is undefined. If there
  16219. are more arguments, they are ignored. The bottom line is that when you use the
  16220. ellipsis in a function prototype you are telling the compiler not to
  16221. type-check your optional arguments because you think you know what you're
  16222. doing -- so be sure that you do.
  16223. The program in Listing 9 shows how to use the va_list mechanism to find the
  16224. largest integer in a variable-length argument list. For more on <stdarg.h>,
  16225. see the Code Capsule, "Variable-length Argument Lists" in the Feb. 1994 issue
  16226. of CUJ.
  16227.  
  16228.  
  16229. Conclusion
  16230.  
  16231.  
  16232. In this three-part series I have attempted to convey the overall flavor of the
  16233. Standard C library, especially in the functionality it provides. It is foolish
  16234. to waste time reinventing this functionality. Although I have classified the
  16235. library into three groups of decreasing priority, my priorities may not match
  16236. yours. If you make your living programming in C or C++, you will do well to
  16237. master the entire library. Enough said.
  16238. Floating-point Number Systems
  16239. The computer's difficulty in manipulating real numbers has long been a source
  16240. of frustration and confusion for students and practitioners alike. The program
  16241. in Listing 1 shows how a simple sequence of sums can go awry. It calculates
  16242. the expression ex by the formula:
  16243. Click Here for Equation
  16244. and compares the result to the "correct" answer given by the exp function in
  16245. the standard library. This works correctly for positive arguments, but the
  16246. result for negative arguments isn't even close! The problem, of course, is
  16247. that computers, with their finite capabilities, can only represent a minuscule
  16248. subset of the set of real numbers. A good chunk of numerical analysis deals
  16249. with roundoff error, the quirks of finite-precision arithmetic.
  16250. In days gone by, many computers used a fixed-point number system, which uses
  16251. numbers derived from a given radix (b), precision (p), and fraction size (f).
  16252. For example, the values b=10, p=4, and f=1 define the following set of 19,999
  16253. numbers:
  16254. F = {-999.9, - 999.8,...,999.8, 999.9}
  16255. Since these numbers are evenly spaced, the maximum absolute error in
  16256. representing any real number x in this system is bounded by 0.05. In other
  16257. words,
  16258.  x-fix(x) Â£ 0.05
  16259. The set of machine integers form a fixed-point system with f=0, and a maximum
  16260. absolute error of 0.5 for systems that round, and no greater than 1.0 for
  16261. systems that truncate.
  16262. Absolute error is not generally useful in mathematical computations, however.
  16263. As is often the case, you are more often interested in percentages, or how one
  16264. number differs in relation to another. You compute the relative error of y
  16265. with respect to x by the following formula:
  16266. Click Here for Equation
  16267. Consider how the numbers 865.54 and 0.86554 are represented in F:
  16268. fix(865.54) = 865.5
  16269. fix(.86554) = 0.9
  16270. Because the number of decimals is fixed, the second is a much poorer fit than
  16271. the first, which the relative errors illustrate:
  16272. Click Here for Equation
  16273. The second is 1,000 times worse than the first!
  16274. Nowadays computers provide floating-point number systems, which represent
  16275. numbers in the form
  16276. ±0.d1d2...dp X be
  16277. where
  16278. m£e£M, 0£di<b
  16279. This is like the scientific notation that you learned in school, except that
  16280. in this case the base (b) can be other than 10, and there is an upper limit on
  16281. the precision (p). Most floating-point systems use a normalized
  16282. representation, which means that d1 cannot be zero. These number systems are
  16283. called floating-point for the obvious reason -- the radix point "floats" and
  16284. the exponent (e) adjusts accordingly to preserve the correct value.
  16285. Consider a floating-point system G defined by b=10, p=4, m=-2, and M=3. The
  16286. numbers from the fixed-point example above are represented as:
  16287. fl(8.65.54)+0.8655 x 103
  16288. fl(865.554)=0.8655 x 100
  16289. Now look what happens when calculating the relative errors of the two
  16290. representations:
  16291. Click Here for Equation
  16292. Click Here for Equation
  16293. It can be shown that the relative error of representing any real number within
  16294. the range of a floating-point system is no greater than b1-p, which is 0.001
  16295. in G.
  16296. Floating-point numbers are not evenly spaced. In G, for example, the next
  16297. number greater than 1 is 1.001, a spacing of 0.001, but the next number after
  16298. 10 is 10.01, which is 0.01 to the right. In general, the spacing among all
  16299. numbers between powers of b, say between be and be+1, is be+1-p. A trivial
  16300. combinatorial analysis shows that each interval [be, be+1] has (b--1) bp-1
  16301. floating-point numbers, and the total number of representable floating-point
  16302. numbers in a system is 2(M--m+1)(b--1)b, pp-1 (so increasing p increases the
  16303. density of the number system). And as shown above, although smaller numbers
  16304. are closer together and larger numbers are farther apart, the relative spacing
  16305. between consecutive floating-point numbers is essentially the same throughout
  16306. the system. In fact, the relative spacing between two adjacent representable
  16307. numbers within an interval [be, be+1] is b1-p, and between be+1 and its
  16308. immediate predecessor is b-p.
  16309. The quantity b1-p, which also happens to be the spacing between 1.0 and its
  16310. immediate successor, is called the machine epsilon (e), and is a good measure
  16311. of the granularity of a floating number system. The smallest floating-point
  16312. magnitude other than zero is of course b, usually denoted as s, and the
  16313. largest magnitude, l, is bM(1--b-p).
  16314. The Standard C header <float.h> provides manifest constants for all of these
  16315. parameters as follows:
  16316. b == FLT_RADIX
  16317. p == DBL_MANT_DIG
  16318. m == DBL_MIN_EXP
  16319. M == DBL_MAX_EXP
  16320. e == DBL_EPSILON
  16321. s == DBL_MIN
  16322. l == DBL_MAX
  16323. <float.h> also provides the float and long double equivalents of the last five
  16324. parameters, representing the three (not necessarily distinct) floating-point
  16325. number systems in standard C (see Table 4).
  16326. The program in Listing 2 calculates a root of x2+x+1 in the interval [--1, 1]
  16327. by the method of bisection. The algorithm halves the interval (which must
  16328. contain a sign change for the function F) until the relative spacing between
  16329. the endpoints is less than or equal to machine epsilon. Such a loop may never
  16330. terminate if you expect greater precision than this.
  16331. Should you find yourself in an environment that does not provide the
  16332. parameters in <float.h>, you can compute b, p, and e directly. To see how this
  16333. is possible, remember that the spacing between consecutive floating-point
  16334. numbers increases with the magnitude of the numbers. Eventually there is a
  16335. point where the spacing between adjacent numbers is greater than 1. (In fact,
  16336. the first interval where this holds true is [bp, bp+1], because the integers
  16337. therein require p+1 digits in their representation.) The positive integers
  16338. precisely representable in a floating-point system are these:
  16339. 1,2,...,bp-1,bp,bp+b,bp+2b,..., bp+1,bp+1+b2,...
  16340.  
  16341. To find the radix, the program in Listing 3 keeps doubling a until it reaches
  16342. a point where the spacing exceeds 1. It then finds the next larger
  16343. floating-point number and subtracts a to get b. The program then computes the
  16344. smallest power of b that will produce numbers whose adjacent spacing exceeds 1
  16345. that power is the precision p. To find e, the program finds the nearest
  16346. representable neighbor to the right of 1, and subtracts 1.
  16347. Table 1 Standard C Headers: Group I (required knowledge for every C
  16348. programmer)
  16349. <ctype.h> Character Handling
  16350. <stdio.h> Input/Output
  16351. <stdlib.h> Miscellaneous Utilities
  16352. <string.h> Text Processing
  16353. Table 2 Standard C Headers: Group 11 (tools for the professional)
  16354. <assert.h> Assertion Support for Defensive Programming
  16355. <limits.h> System Parameters for Integer Arithmetic
  16356. <stddef.h> Universal Types & Constant
  16357. <time.h> Time Processing
  16358. Table 3 Standard C Headers: Group III (power at your fingertips when you need
  16359. it)
  16360. <errno.h> Error Detection
  16361. <float.h> System Parameters for Real Arithmetic
  16362. <locale.h> Cultural Adaptation
  16363. <math.h> Mathematical Functions
  16364. <setjmp.h> Non-local Branching
  16365. <signal.h> Interrupt Handling (sort of)
  16366. <stdarg.h> Variable-length Argument Lists
  16367. Table 4 Definitions in <float.h>
  16368. Parameter Meaning "Minimum"
  16369.  Value
  16370. --------------------------------------------------------------
  16371. FLT_RADIX exponent base 2
  16372. FLT_ROUNDS rounding mode (see Table 5)
  16373. FLT_MANT_DIG precision for float
  16374. DBL_MANT_DIG precision for double
  16375. LDBL_MANT_DIG precision for long double
  16376. FLT_DIG base-10 precision for float 6
  16377. DBL_DIG same for double 10
  16378. LDBL_DIG same for long double 10
  16379. FLT_MIN_EXP min. exponent for float
  16380. DBL_MIN_EXP same for double
  16381. LDBL_MIN_EXP same for long double
  16382. FLT_MIN_10_EXP min. base-10 float exponent -37
  16383. DBL_MIN_10_EXP same for double -37
  16384. LDBL_MIN_10_EXP same for long double -37
  16385. FLT_MIN smallest float 1E-37
  16386. DBL_MIN smallest double 1E-37
  16387. LDBL_MIN smallest long double 1E-37
  16388. FLT_MAX_EXP max. exponent for float
  16389. DBL_MAX_EXP same for double
  16390. LDBL_MAX_EXP same for long double
  16391. FLT_MAX_10_EXP max. base-10 float exponent +37
  16392. DBL_MAX_10_EXP same for double +37
  16393. LDBL_MAX_10_EXP same for long double +37
  16394. FLT_MAX largest float 1E+37
  16395. DBL_MAX largest double 1E+37
  16396. LDBL_MAX largest long double 1E+37
  16397. FLT_EPSILON machine epsilon for float 1E-5
  16398. DBL_EPSILON same for double 1E-9
  16399. LDBL_EPSILON same for long double 1E-9
  16400. Table 5 Values for FLT_Rounds
  16401. -1 indeterminable
  16402.  0 toward zero
  16403.  1 to nearest
  16404.  2 toward positive infinity
  16405.  3 toward negative infinity
  16406. Table 6 Functions defined in <math.h>
  16407.  
  16408. acos arc-osine
  16409. asin arc-sine
  16410. atan arc-tangent (principal value)
  16411. atan2 arc-tangent (full circle)
  16412. ceil ceiling
  16413. cos cosine
  16414. cosh hyperbolic cosine
  16415. exp power of e
  16416. fabs absolute value
  16417. floor floor (greatest-integer-in
  16418.  function)
  16419. fmod modulus (remainder)
  16420. frexp normalized fraction/
  16421.  exponent parts
  16422. ldexp inverse of frexp
  16423. log natural logarithm
  16424. log10 logarithm base 10
  16425. modf integer/fractional parts
  16426. pow raise a number to a power
  16427. sin sine
  16428. sinh hyperbolic sine
  16429. sqrt square root
  16430. tan tangent
  16431. tanh hyperbolic tangent
  16432. Table 7 Definitions in <errno.h>
  16433. errno Global integer to report
  16434.  certain errors
  16435. EDOM Domain error code
  16436. ERANGE Range error code
  16437. Table 8 Locale Categories
  16438. Category Functionality
  16439. ----------------------------------------------------------------------
  16440. LC_COLLATE Adapts strcoll, strxfrm, wcscoll, and wcsxfrm to the
  16441.  language/culture of the locale.
  16442. LC_CTYPE Adapts the isxxx/iswxxx functions in ctype.h to the
  16443.  character set associated with the locale, and affects
  16444.  multibyte/wide character mapping.
  16445. LC_MONETARY Sets parameters pertaining to the display of monetary
  16446.  values such as decimal point, grouping (e.g., thousands),
  16447.  group separator, etc. This category is purely advisory and
  16448.  affects no standard library functions.
  16449. LC_NUMERIC Like LC_MONETARY, but for non-monetary values (e.g., the
  16450.  decimal point may be different than for non-monetary
  16451.  values).
  16452. LC_TIME Adapts strftime and wcsftime to cultural specifications.
  16453. Table 9 Definitions in <signal.h>
  16454. Signal Macros
  16455. -----------------------------------------------
  16456. SIGABRT abnormal termination
  16457.  (raised by abort)
  16458. SIGFPE computational exception
  16459.  (e.g., overflow)
  16460. SIGILL invalid function image
  16461.  (e.g., illegal instruction)
  16462. SIGINT interactive attention (e.g.,
  16463.  Control-C)
  16464. SIGSEGV attempt to access protected
  16465.  memory
  16466. SIGTERM termination request
  16467.  
  16468.  
  16469. Functions
  16470. -----------------------------------------------
  16471. raise generates a signal from
  16472.  within a program
  16473. signal registers a function as a
  16474.  signal-handler
  16475. Table 10 Definitions in <stdarg.h>
  16476. Type
  16477. ---------------------------------------------
  16478. va_list A variable-length argument list
  16479.  
  16480. Macros
  16481. -----------------------------------------------
  16482. va_start Initializes a va_list
  16483. va_arg Gets next arg in a va_list
  16484. va_end Closes a va_list
  16485.  
  16486. Listing 1 Shows roundoff error in computing powers of e
  16487. #include <stdio.h>
  16488. #include <math.h>
  16489.  
  16490. double e(double x);
  16491.  
  16492. main()
  16493. {
  16494. printf("e(55.5) == %g, exp(55.5) == %g\n",
  16495. e(55.5), exp(55.5));
  16496. printf("e(-55.5) == %g, exp(-55.5) == %g\n",
  16497. e(-55.5), exp(-55.5));
  16498. printf("1/e(55.5) == %g\n",1.0 / e(55.5));
  16499. return 0;
  16500. }
  16501.  
  16502. double e(double x)
  16503. {
  16504. double sum1 = 1.0;
  16505. double sum2 = 1.0 + x;
  16506. double term = x;
  16507. int i = 1;
  16508.  
  16509. /* Calculate exp(x) via Taylor Series */
  16510. while (sum1 != sum2)
  16511. {
  16512. sum1 = sum2;
  16513. term = term * x / ++i;
  16514. sum2 += term;
  16515. }
  16516. return sum2;
  16517. }
  16518.  
  16519. /* Output:
  16520. e(55.5) == 1.26866e+24, exp(55.5) == 1.26866e+24
  16521. e(-55.5) == -6.76351e+06, exp(-55.5) == 7.88236e-25
  16522. 1/e(55.5) == 7.88236e-25
  16523. */
  16524.  
  16525. /* End of File */
  16526.  
  16527.  
  16528.  
  16529. Listing 2 Illustrates use of machine epsilon in a root-finding algorithm
  16530. /* root.c:
  16531. *
  16532. * To use a different precision, change ftype
  16533. * and the suffix of the floating-point constants
  16534. */
  16535.  
  16536. #include <stdio.h>
  16537. #include <float.h>
  16538. #include <math.h>
  16539. #include <assert.h>
  16540.  
  16541. #define sign(x) ((x < 0.0) ? -1 : (x > 0.0) ? 1 : 0)
  16542.  
  16543. #define PREC DBL_DIG
  16544. #define EPS DBL_EPSILON
  16545.  
  16546. typedef double ftype;
  16547.  
  16548. ftype root(ftype a, ftype b, ftype (*f)(ftype))
  16549. {
  16550.  
  16551. ftype fofa = f(a);
  16552. ftype fofb = f(b);
  16553. assert(a < b);
  16554. assert(fofa * fofb < 0.0);
  16555.  
  16556. /* Close-in on root via bisection */
  16557. while (fabs(b - a) > EPS*fabs(a))
  16558. {
  16559. ftype x = a + (b-a)/2.0;
  16560. ftype fofx = f(x);
  16561. if (x <= a x >= b fofx == 0.0)
  16562. return x;
  16563. if (sign(fofx) == sign(fofa))
  16564. {
  16565. a = x;
  16566. fofa = fofx;
  16567. }
  16568. else
  16569. {
  16570. b = x;
  16571. fofb = fofx;
  16572. }
  16573. }
  16574. return a;
  16575. }
  16576.  
  16577. main()
  16578. {
  16579. extern ftype f(ftype);
  16580. printf("root == %.*f\n",PREC,root(-1.0,1.0,f));
  16581. return 0;
  16582. }
  16583.  
  16584. ftype f(ftype x)
  16585. {
  16586. return x*x + x - 1.0;
  16587.  
  16588. }
  16589.  
  16590. /* Output:
  16591. root == 0.618033988749895
  16592. */
  16593. /* End of File */
  16594.  
  16595.  
  16596. Listing 3 Computes Machine Floating-point Parameters
  16597. #include <stdio.h>
  16598. #include <math.h>
  16599. #include <float.h>
  16600.  
  16601. main()
  16602. {
  16603. int beta, p;
  16604. double a, b, eps, epsp1, sigma, nums;
  16605.  
  16606. /* Discover radix */
  16607. a = 1.0;
  16608. do
  16609. {
  16610. a = 2.0 * a;
  16611. b = a + 1.0;
  16612. } while ((b - a) == 1.0);
  16613.  
  16614. b = 1.0;
  16615. do
  16616. b = 2.0 * b;
  16617. while ((a + b) == a);
  16618. beta = (int) ((a + b) - a);
  16619. printf("radix:\n");
  16620. printf("\talgorithm: %d\n",beta);
  16621. printf("\tprovided: %d\n",FLT_RADIX);
  16622.  
  16623. /* Compute precision in bits */
  16624. p = 0;
  16625. a = 1.0;
  16626. do
  16627. {
  16628. ++p;
  16629. a *= (double) beta;
  16630. b = a + 1.0;
  16631. } while ((b - a) == 1.0);
  16632. printf("precision:\n");
  16633. printf("\talgorithm: %d\n",p);
  16634. printf("\tprovided: %d\n",DBL_MANT_DIG);
  16635.  
  16636. /* Compute machine epsilon */
  16637. eps = 1.0;
  16638. do
  16639. {
  16640. eps = 0.5 * eps;
  16641. epsp1 = eps + 1.0;
  16642. } while (epsp1 > 1.0);
  16643. epsp1 = 2.0 * eps + 1.0;
  16644. eps = epsp1 - 1.0;
  16645. printf("machine epsilon:\n");
  16646. printf("\talgorithm: %g\n",eps);
  16647.  
  16648. printf("\tformula: %g\n",pow(FLT_RADIX,1.0-DBL_MANT_DIG));
  16649. printf("\tprovided: %g\n",DBL_EPSILON);
  16650.  
  16651. /* Compute smallest normalized magnitude */
  16652. printf("smallest non-zero magnitude:\n");
  16653. printf("\tformula: %g\n",pow(FLT_RADIX,DBL_MIN_EXP-1));
  16654. printf("\tprovided: %g\n",DBL_MIN);
  16655.  
  16656. /* Compute larget normalized magnitude */
  16657. printf("largest non-zero magnitude:\n");
  16658. printf("\tformula: %g\n",
  16659. pow(FLT_RADIX,DBL_MAX_EXP-1) * FLT_RADIX *
  16660. (1.0 - pow(FLT_RADIX,-DBL_MANT_DIG)));
  16661. printf("\tprovided: %g\n",DBL_MAX);
  16662.  
  16663. printf("smallest exponent: %d\n",DBL_MIN_EXP);
  16664. printf("largest exponent: %d\n",DBL_MAX_EXP);
  16665.  
  16666. nums = 2 * (FLT_RADIX - 1)
  16667. * pow(FLT_RADIX,DBL_MANT_DIG-1)
  16668. * (DBL_MAX_EXP - DBL_MIN_EXP + 1);
  16669. printf("This system has %g numbers\n",nums);
  16670. return 0;
  16671. }
  16672.  
  16673. /* Output (Borland C++):
  16674. radix:
  16675. algorithm: 2
  16676. provided: 2
  16677. precision:
  16678. algorithm: 53
  16679. provided: 53
  16680. machine epsilon:
  16681. algorithm: 2.22045e-16
  16682. formula: 2.22045e-16
  16683. provided: 2.22045e-16
  16684. smallest non-zero magnitude:
  16685. formula: 2.22507e-308
  16686. provided: 2.22507e-308
  16687. largest non-zero magnitude:
  16688. formula: 1.79769e+308
  16689. provided: 1.79769e+308
  16690. smallest exponent: -1021
  16691. largest exponent: 1024
  16692. This system has 1.84287e+19 numbers
  16693. */
  16694. /* End of File */
  16695.  
  16696.  
  16697. Listing 4 Illustrates several <math.h> functions
  16698. #include <stdio.h>
  16699. #include <math.h>
  16700.  
  16701. main()
  16702. {
  16703. double x = 1234.56, y = 90.1234, z, w;
  16704. int p;
  16705.  
  16706. printf("x == %g, y == %g\n",x,y);
  16707.  
  16708. printf("fmod(x,y) == %g\n",fmod(x,y));
  16709. printf("floor(y) == %g\n",floor(y));
  16710. printf("ceil(y) == %g\n",ceil(y));
  16711. w = modf(y,&z);
  16712. printf("after modf(y,&z): w == %g, z == %g\n",w,z);
  16713. w = frexp(y,&p);
  16714. printf("after frexp(y,&p): w == %g, p == %d\n",w,p);
  16715. printf("ldexp(w,p) == %g\n",ldexp(w,p));
  16716. return 0;
  16717. }
  16718.  
  16719. /* Output:
  16720. x == 1234.56, y == 90.1234
  16721. fmod(x,y) == 62.9558
  16722. floor(y) == 90
  16723. ceil(y) == 91
  16724. after modf(y,&z): w == 0.1234, z == 90
  16725. after frexp(y,&p): w == 0.704089, p == 7
  16726. ldexp(w,p) == 90.1234
  16727. */
  16728. /* End of File */
  16729.  
  16730.  
  16731. Listing 5 A simple calculator that illustrates some <math.h> functions
  16732. /* calc.c: Lame-brain Calculator-
  16733. *
  16734. * For simplicity in parsing, this program
  16735. * reads lines of the form:
  16736. *
  16737. * value operation
  16738. *
  16739. * where the value is optional in some cases.
  16740. * For example, the following script computes
  16741. * the integer part of sqrt(1 + 3.4*3.4):
  16742. *
  16743. * 3.4 =
  16744. * 3.4 *
  16745. * 1 +
  16746. * sqrt
  16747. * floor
  16748. */
  16749.  
  16750. #include <stdio.h>
  16751. #include <stdlib.h>
  16752. #include <math.h>
  16753. #include <string.h>
  16754. #include <ctype.h>
  16755. #include <errno.h>
  16756.  
  16757. #define LINSIZ 40
  16758.  
  16759. char *getline(char *);
  16760.  
  16761. main()
  16762. {
  16763. double reg = 0.0;
  16764. char line[LINSIZ];
  16765. while (getline(line) != NULL)
  16766. {
  16767.  
  16768. char *op;
  16769. double val;
  16770.  
  16771. /* Parse command string */
  16772. val = strtod(line,&op);
  16773. while (isspace(*op))
  16774. ++op;
  16775. strupr(op);
  16776.  
  16777. /* Perform operation */
  16778. errno = 0;
  16779. if (*op == '+')
  16780. reg += val;
  16781. else if (*op == '-')
  16782. reg -= val;
  16783. else if (*op == '*')
  16784. reg *= val;
  16785. else if (*op == '/')
  16786. {
  16787. if (val != 0)
  16788. reg /= val;
  16789. else
  16790. {
  16791. puts("ERROR>>> invalid divisor");
  16792. continue;
  16793. }
  16794. }
  16795. else if (*op == '=')
  16796. reg = val;
  16797. else if (*op == '^')
  16798. {
  16799. if (val == 0.0)
  16800. reg = 1.0;
  16801. else if (val == 0.5)
  16802. reg = sqrt(reg);
  16803. else
  16804. reg = pow(reg,val);
  16805. }
  16806. else if (strncmp(op,"NEGATE",1) == 0)
  16807. reg = -reg;
  16808. else if (strncmp(op,"MOD",1) == 0)
  16809. {
  16810. if (val == 0.0)
  16811. {
  16812. puts("ERROR>>> invalid modulus");
  16813. continue;
  16814. }
  16815. else
  16816. reg = fmod(reg,val);
  16817. }
  16818. else if (strncmp(op,"CEIL",1) == 0)
  16819. reg = ceil(reg);
  16820. else if (strncmp(op,"FLOOR",1) == 0)
  16821. reg = floor(reg);
  16822. else if (strncmp(op,"ROUND",1) == 0)
  16823. reg = (reg < 0.0) ? ceil(reg - 0.5)
  16824. : floor(reg + 0.5);
  16825. else if (strncmp(op,"SQRT",1) == 0)
  16826. reg = sqrt(reg);
  16827.  
  16828. else if (strncmp(op,"QUIT",1) == 0)
  16829. exit(0);
  16830. else if (*op != '\0')
  16831. {
  16832. puts("ERROR>>> invalid operation");
  16833. continue;
  16834. }
  16835.  
  16836. if (errno)
  16837. perror("ERROR>>>");
  16838. else
  16839. printf("\t%s => %g\n",line,reg);
  16840. }
  16841. return 0;
  16842. }
  16843.  
  16844. char *getline(char *buf)
  16845. {
  16846. fputs ("Calc> ",stdout);
  16847. fflush(stdout);
  16848. return gets(buf);
  16849. }
  16850.  
  16851. /* Output:
  16852. Calc> 3.4 =
  16853. 3.4 = => 3.4
  16854. Calc> 3.4 *
  16855. 3.4 * => 11.56
  16856. Calc> 1 +
  16857. 1+ => 12.56
  16858. Calc> sqrt
  16859. SQRT => 3.54401
  16860. Calc> floor
  16861. FLOOR => 3
  16862. Calc> q
  16863. */
  16864.  
  16865. /* End of file */
  16866.  
  16867.  
  16868. Listing 6 The members of struct lconv
  16869. struct lconv
  16870. {
  16871. char *decimal_point;
  16872. char *thousands_sep;
  16873. char *grouping;
  16874. char *int_curr_symbol;
  16875. char *currency_symbol;
  16876. char *mon_decimal_point;
  16877. char *mon_thousands_sep;
  16878. char *mon_grouping;
  16879. char *positive_sign;
  16880. char *negative_sign;
  16881. char int_frac_digits;
  16882. char frac_digits;
  16883. char p_cs_precedes;
  16884. char p_sep_by_space;
  16885. char n_cs_precedes;
  16886. char n_sep_by_space;
  16887.  
  16888. char p_sign_posn;
  16889. char n_sign_posn;
  16890. };
  16891.  
  16892.  
  16893. Listing 7 Shows the effect of locale settings on the decimal point character
  16894. and time formatting
  16895. /* tlocale.c: Illustrates locales-
  16896. *
  16897. * Compiled in Visual C++ under Windows NT 3.5
  16898. */
  16899.  
  16900. #include <locale.h>
  16901. #include <stdio.h>
  16902. #include <time.h>
  16903.  
  16904. void print_stuff(void);
  16905.  
  16906. main()
  16907. {
  16908. /* First in the C locale */
  16909. puts("In the C locale:");
  16910. print_stuff();
  16911.  
  16912. /* Now try German */
  16913. puts("\nIn the German locale:");
  16914. setlocale(LC_ALL,"german");
  16915. print_stuff();
  16916.  
  16917. /* Now try American */
  16918. puts("\nIn the American locale:");
  16919. setlocale(LC_ALL,"american");
  16920. print_stuff();
  16921.  
  16922. /* Now try Italian */
  16923. puts("\nIn the Italian locale:");
  16924. setlocale(LC_ALL,"italian");
  16925. print_stuff();
  16926. return 0;
  16927. }
  16928.  
  16929. void print_stuff(void)
  16930. {
  16931. char text[81];
  16932. time_t timer = time(NULL);
  16933. struct lconv *p = localeconv();
  16934.  
  16935. printf("decimal pt. == %s\n",
  16936. p->decimal_point);
  16937. printf("currency symbol == %s\n",
  16938. p->int_curr_symbol);
  16939. printf("%.2f\n",l.2);
  16940. strftime(text,sizeof text,"%A, %B, %d,
  16941. %Y (%x)\n", localtime(&timer));
  16942. puts(text);
  16943. }
  16944.  
  16945. In the C locale:
  16946. decimal pt. == ,
  16947. currency symbol ==
  16948.  
  16949. 1.20
  16950. Tuesday, January 03, 1995 (01/03/95)
  16951.  
  16952.  
  16953. In the German locale:
  16954. decimal pt. == .
  16955. currency symbol == DEM
  16956. 1,20
  16957. Dienstag, Januar 03, 1995 (03.01.95)
  16958.  
  16959.  
  16960. In the American locale:
  16961. decimal pt. == .
  16962. currency symbol == USD
  16963. 1.20
  16964. Tuesday, January 03, 1995 (01/03/95)
  16965.  
  16966.  
  16967. In the Italian locale:
  16968. decimal pt. == ,
  16969. currency symbol == ITL
  16970. 1,20
  16971. marted, gennaio 03, 1995 (03/01/95)
  16972. /* End of File */
  16973.  
  16974.  
  16975. Listing 8 Shows how to register a signal
  16976. /* ignore.c: Ignores interactive attention
  16977. key as */
  16978. #include <stdio.h>
  16979. #include <signal.h>
  16980.  
  16981. main()
  16982. {
  16983. char buf[BUFSIZ];
  16984. int i;
  16985.  
  16986. /* Ignore keyboard interruptions */
  16987. signal(SIGINT,SIG_IGN);
  16988.  
  16989. while (gets(buf))
  16990. puts(buf);
  16991.  
  16992. /* Restore default attention key handling
  16993. so the user can abort form the keyboard */
  16994. signal(SIGINT,SIG_DFL);
  16995. for (i = 1; ; ++i)
  16996. printf("%d%c",i,(i%15 ? ' ' : '\n'));
  16997. }
  16998.  
  16999. /* Sample Execution
  17000. c:>ignore
  17001. hello
  17002. hello
  17003. ^C
  17004. there
  17005. there
  17006. ^C
  17007. ^Z
  17008.  
  17009. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  17010. 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
  17011. 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
  17012. 46 45 48 49 50 51 52 53 54 55 56 57 58 59 60
  17013. 61 62 63 64 65 66 67 ^C
  17014. */
  17015. /* End of file*/
  17016.  
  17017.  
  17018. Listing 9 Uses the <stdarg.h> macros to search a variable-length list of
  17019. integers 
  17020. /* max.c */
  17021. #include <stdio.h>
  17022. #include <stdarg.h>
  17023.  
  17024. int maxn(size_t count, ...)
  17025. {
  17026. int n, big;
  17027. va_list numbers;
  17028.  
  17029. va_start(numbers,count);
  17030.  
  17031. big = va_arg(numbers,int);
  17032. while (count--)
  17033. {
  17034. n = va_arg(numbers,int);
  17035. if (n > big)
  17036. big = n;
  17037. }
  17038.  
  17039. va_end(numbers);
  17040. return big;
  17041. }
  17042.  
  17043. main()
  17044. {
  17045. printf("max = %d\n",maxn(3,1,3,2));
  17046. return 0;
  17047. }
  17048.  
  17049. /* Output:
  17050. max = 3
  17051. */
  17052.  
  17053. /* End of File */
  17054.  
  17055.  
  17056.  
  17057.  
  17058.  
  17059.  
  17060.  
  17061.  
  17062.  
  17063.  
  17064.  
  17065.  
  17066.  
  17067.  
  17068.  
  17069.  
  17070.  
  17071.  
  17072.  
  17073. Stepping Up To C++
  17074.  
  17075.  
  17076. More Minor Enhancements as of CD Registration
  17077.  
  17078.  
  17079.  
  17080.  
  17081. Dan Saks
  17082.  
  17083.  
  17084. Dan Saks is the president of Saks & Associates, which offers consulting and
  17085. training in C++ and C. He is secretary of the ANSI and ISO C++ committees. Dan
  17086. is coauthor of C++ Programming Guidelines, and codeveloper of the Plum Hall
  17087. Validation Suite for C++ (both with Thomas Plum). You can reach him at 393
  17088. Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601, or
  17089. electronically at dsaks@wittenberg.edu.
  17090.  
  17091.  
  17092. For the past two months I've been listing ways that the programming language
  17093. described by the C++ draft standard (as of Fall, 1994) differs from the
  17094. language described in the ARM [1]. Two months ago, I described the major
  17095. extensions:
  17096. Templates
  17097. Exception handling
  17098. Run-time type information (including dynamic_cast)
  17099. Namespaces
  17100. (See "Stepping Up to C++: C++ at CD Registration," CUJ, January 1995.) Last
  17101. month, I started describing the minor enhancements, and managed to cover the
  17102. following:
  17103. New keywords and digraphs for C++ as alternate ISO646-compliant spellings for
  17104. existing tokens
  17105. Operator overloading on enumerations
  17106. operator new[] and operator delete[]
  17107. Relaxed restrictions on the return type of virtual functions
  17108. wchar_t as a keyword representing a distinct type
  17109. A built-in Boolean type
  17110. Declarations in conditional expressions
  17111. This month, I continue by describing a few more of the remaining minor
  17112. enhancements.
  17113.  
  17114.  
  17115. New cast notation
  17116.  
  17117.  
  17118. The extensions to support RTTI (run-time type information) included a new type
  17119. conversion operator called dynamic_cast. An expression such as
  17120. dynamic_cast<T *>(p)
  17121. yields the result of casting (converting) pointer p to type T *. If p points
  17122. to a T object, the result is p. If p points to an object of a type D derived
  17123. from T, then the result is a pointer to the unique T sub-object of the D
  17124. object addressed by p. (In fact, these conversions do not even require a
  17125. cast.) Otherwise, p must point to an object of a polymorphic type (a type with
  17126. at least one virtual function), in which case the resulting program performs a
  17127. run-time check.
  17128. The run-time check is this: If p points to a base class sub-object of a T
  17129. object, the result is a pointer to that T object; otherwise, it yields a null
  17130. pointer. (This description of the run-time check is over-simplified, but it
  17131. covers the most common and useful cases, namely public inheritance from a
  17132. single direct base class.) Listing 1 shows an example of this sort of
  17133. conversion, commonly called a downcast.
  17134. dynamic_cast can also perform reference conversions. An expression such as
  17135. dynamic_cast<T &>(r)
  17136. converts reference r to type T &. The rules for dynamic_cast applied to
  17137. references parallel the rules when applied to pointers, except that a
  17138. reference conversion throws an exception, rather than return null, when it
  17139. fails.
  17140. The dynamic_cast notation is obviously, and intentionally, different from the
  17141. "old-style" cast notation, (T)e, which converts expression e to type T. The
  17142. proponents of RTTI did not want to confuse this new functionality with
  17143. existing conversions by using the same syntax.
  17144. In the course of distinguishing dynamic_cast from the other casts, the C++
  17145. standards committees generally agreed with Bjarne Stroustrup that the
  17146. old-style cast lumps too many different kinds of conversions under a single
  17147. blanket notation [2]. Among other things, an old-style cast can do the
  17148. following:
  17149. narrow or widen an arithmetic value,
  17150. discard a const or volatile attribute from an expression,
  17151. perform implicit address arithmetic while converting B * to D * (where D is
  17152. derived from B), or
  17153. reinterpret the value of an expression of one type as a value with a
  17154. completely different type.
  17155. Stroustrup also argued that the old-style cast notation is too indistinct.
  17156. Old-style casts are just one of many uses for parentheses in C and C++. These
  17157. casts are hard for humans to spot in source code, and hard to find using tools
  17158. like grep.
  17159. The standards committees accepted Stroustrup's proposal to add three new cast
  17160. operators in the mold of dynamic_cast. These casts do not add any new
  17161. functionality to C++. They simply classify the functionality of old-style
  17162. casts into three distinct categories:
  17163. static_cast<T>(e) is for the better-behaved conversions, such as from one
  17164. arithmetic type to a narrower wider arithmetic type or to an enumeration, from
  17165. a pointer-to-base (reference- to-base) to pointer-to-derived
  17166. (reference-to-derived), or from any type to void. static_cast can add, but not
  17167. remove cv- qualifiers (const and volatile).
  17168. reinterpret_cast<T>(e) is for the poorly-behaved conversions (those with
  17169. implementation-dependent behavior), such as converting from an integral type
  17170. to a pointer or vice versa, to or from a pointer to an incomplete type, or
  17171. from one pointer-to-function type to another.
  17172. const_cast<T>(e) is for converting a type with cv-qualifiers to a type with
  17173. fewer cv-qualifiers.
  17174. For compatibility with C and existing C++ code, the committees did not remove,
  17175. nor even deprecate, the old-style casts. (A deprecated feature is one that has
  17176. fallen into disfavor and may disappear altogether from some future version of
  17177. the standard.) The new-style casts are just a more explicit alternative
  17178. notation.
  17179. For example, the implementation of the Standard C function bsearch typically
  17180. involves converting a parameter from const void * to const char *, and
  17181. returning it as void *. Plauger's implementation [3] uses an old style cast in
  17182. the return statement
  17183. return ((void *)q);
  17184. to convert q from const char *to void *. The current C++ view is that this
  17185. cast actually performs two distinct conversions:
  17186. 1. It converts the type from char *to void *.
  17187. 2. It removes ("casts-away") a const qualifier. It's not apparent from a
  17188. casual reading of that return statement that the (void *) cast removes a const
  17189. qualifier.
  17190.  
  17191. Plauger wrote his library in C, so he had no choice about the cast notation,
  17192. but a C++ programmer can now write the conversion more explicitly using the
  17193. new style-casts:
  17194. return static_cast<void *>(const_cast<char *>(q));
  17195. Writing the return as
  17196. return static_cast<void *>(q);
  17197. is an error, because static_cast cannot cast-away a const qualifier. Writing
  17198. return const_cast<void *>(q);
  17199. is also an error, because a const_cast can only change the cv-qualification,
  17200. not the type.
  17201. For much more about the new-style casts, see [2].
  17202.  
  17203.  
  17204. Qualified Names in Elaborated-Type-Specifiers
  17205.  
  17206.  
  17207. C places struct, union, and enum tags in a namespace separate from ordinary
  17208. identifiers (those that designate functions, objects, typedefs, and
  17209. enumeration constants). For example, the declaration
  17210. struct X { ... };
  17211. enters X as a struct name in the tag namespace. Because X is a tag, not a
  17212. type, you cannot write declarations such as
  17213. X *p;
  17214. You must write the declaration as
  17215. struct X *p;
  17216. Some C programmers take advantage of this distinction between tags and
  17217. ordinary identifiers, and write declarations like
  17218. struct X { ... } X;
  17219. which declares a struct X, and then an object X of type struct X, all in one
  17220. declaration. Other C programmers, including yours truly, deem this bad
  17221. practice, preferring to think of tags as type names.
  17222. I always equate each tag name to a type name using a typedef. A declaration of
  17223. the form
  17224. typedef struct X { ... } X;
  17225. often does the trick. Unfortunately, this doesn't introduce the typedef name
  17226. until after the body of the struct. I prefer writing such declarations in the
  17227. form:
  17228. typedef struct X X;
  17229. struct X { ... };
  17230. C++ treats tags differently from C. In C++, a class name is still a tag name,
  17231. but it is also a type name. In fact, C++ treats all tags (for classes,
  17232. structs, unions, and enums) as types. Thus, given
  17233. struct X { ... };
  17234. C++ lets you use X as an ordinary identifier designating a type, as in
  17235. X *p;
  17236. For compatibility with C, C++ still accepts
  17237. struct X *p;
  17238. In C++, the combination of one of the keywords class, struct, union or enum,
  17239. followed by a tag, is called an elaborated-type-specifier.
  17240. Again in the name of compatibility with C, C++ tolerates declarations such as
  17241. struct X { ... } X;
  17242. In this case, the object name X hides the type name X, so that subsequent uses
  17243. of the unelaborated name X refer to the object. However, you can refer to the
  17244. type name by using its elaborated form struct X, or by using X in a qualified
  17245. name such as X::m (where m is a member of X).
  17246. Now, with this background in mind, consider the declaration in Listing 2.
  17247. Here, B appears as two different members of A: as a type and as a data member.
  17248. The data member hides the type, so that outside A, A::B refers to the data
  17249. member, not the nested type. You can refer to B as a type by using it in a
  17250. qualified name like A::B::n, because C++ looks up a name followed by :: as if
  17251. that name were a type. But how do you refer to B itself as a type?
  17252. For example, given the declaration in Listing 2, a declaration such as
  17253. A::B *pb;
  17254. is an error, because the data member name B hides the type name B in the scope
  17255. of A. In fact, the ARM provide no notation for referring to A::B as a member
  17256. type.
  17257. The committees rectified this minor problem with a minor grammatical extension
  17258. to allow a class-key (the keyword class, struct, or union) in an
  17259. elaborated-type-specifier. For example, you can now write
  17260. struct A::B *pb;
  17261. to declare pb as a pointer to an object of type A::B. Notice that the keyword
  17262. struct elaborates B, not A. It's already clear that A designates a type in
  17263. this context because A is followed by ::.
  17264. This extension also permits the keyword enum in an elaborated-type-specifier.
  17265. For example,
  17266. enum A::E *pe;
  17267. declares pe as a pointer to an object of enumeration type A::E.
  17268.  
  17269.  
  17270. Expressions of the form a.::B::c and p->::B::c
  17271.  
  17272.  
  17273. The committees straggled for several years to correct problems in the ARM's
  17274. specification of scope and name lookup rules. The ARM's rules are incomplete,
  17275. and at various times imprecise and contradictory. (See "Stepping Up to C++:
  17276. Looking Up Names," CUJ, August 1993 and "Stepping Up to C++: Rewriting and
  17277. Reconsidering," CUJ, September 1993.)
  17278. One of the last problems in this area was to pin down the precise meaning of
  17279. expressions of the form x.B::c (or p->B::c). B might be as simple as an
  17280. identifier or as complicated as a template expression. How does a C++ compiler
  17281. evaluate (look up) B in such expressions? The answer to this question covers
  17282. more complicated expressions such as x.B::C::d, because once the compiler
  17283. knows what B denotes, it can easily resolve C and d.
  17284. After considering different rules for looking up B in x.B::c, the committee
  17285. narrowed the choice to two possibilities:
  17286. 1. Look up B in the context where it appears (known as the "golf" rule because
  17287. you "play it where it lies")
  17288. 2. Look up B as if it were in the body of a member function of x. That is,
  17289. look in x's class (and its bases), then look in x's lexically enclosing class
  17290. (and its bases), and so on out to global scope.
  17291. There are arguments in favor of both rules. Suppose you have a class with an
  17292. inconveniently long name, such as class VeryLongName shown in Listing 3. You
  17293. can write terser code by defining a shorter alias for the class name, such as:
  17294. typedef VeryLongName VLN;
  17295.  
  17296. When a C++ compiler evaluates
  17297. ap->VLN::f();
  17298. in function g of Listing 3, it won't find VLN if it only looks in the scope of
  17299. A (which is the scope of *ap). This is especially true if you rearrange lines
  17300. // 1 and // 2, as shown in Listing 4. This aliasing technique only works if
  17301. C++ applies the golf rule (rule 1).
  17302. On the other hand, rule (2) supports different programming techniques that can
  17303. also be useful. Sometimes, the author of a derived class may wish to provide
  17304. users access to hidden names in the base class, without requiring that users
  17305. know the name of the base class. This gives class designers a little more
  17306. flexibility to change the class hierarchy without forcing the users to also
  17307. change their code. The example in Listing 5 illustrates the problem and a
  17308. solution.
  17309. In Listing 5, D::f() hides inherited member B::f(). Normally, the function
  17310. call in
  17311. void g(D *pd)
  17312. {
  17313. pd->f();
  17314. ...
  17315. }
  17316. applies D::f() to the D object addressed by pd. Function g could call B::f()
  17317. with a call such as
  17318. pd->B::f();
  17319. But this requires that g "know" the base class by name. The alternative (shown
  17320. in Listing 5) is for D to define
  17321. typedef B inherited;
  17322. as a type member, so that g can call
  17323. pd->inherited::f();
  17324. Stroustrup [2] discusses this technique in greater detail. It only works if
  17325. C++ compilers look up inherited in the scope of the class of *pd, namely D,
  17326. using rule 2. Looking in the context of the call itself (rule 1) defeats this
  17327. technique.
  17328. The committees never found a convincing case for choosing one lookup rule over
  17329. the other. Thus they decided that C++ should use both lookup rules, and that
  17330. the combined result of both lookups should yield only one type. If the lookups
  17331. yield conflicting results, the expression is ambiguous. The following is a
  17332. more precise statement.
  17333. In a postfix-expression of the form x.B::c, the translator looks for B as a
  17334. type (looking only for types) in two contexts:
  17335. 1. the class scope of x (that is, look up B as if it were a member of x), and
  17336. 2. the context in which the entire postfix expression appears (the context in
  17337. which the translator looked for x itself).
  17338. The combined set of types found by (1) and (2) must have exactly one element;
  17339. that element is the meaning of B. If the translator finds nothing, then it
  17340. treats B as undeclared. If it finds more than one match, the reference is
  17341. ambiguous.
  17342. There is precedent for this approach to name lookup in other parts of C++.
  17343. When a C++ translator encounters an expression of the form x @ y, where x is
  17344. an object of a class type and @ is an overloadable operator, it looks for an
  17345. operator@ that satisfies either
  17346. x.operator@(y)
  17347. or
  17348. operator@(x, y)
  17349. The search must find exactly one match. If it finds more the one, the
  17350. expression is ambiguous.
  17351. Now, at long last, I get to explain the extension itself. As part of
  17352. clarifying the lookup rules, the committee decided to grant C++ programmers a
  17353. little more control over name lookup for B in x.B::c. The extension allows
  17354. postfix expressions of the form
  17355. a.::B::c, a.::B::C::d, etc.
  17356. p->::B::c, p->::B::C::d, etc.
  17357. That is, C++ now allows :: after the . or ->. In such expressions, the
  17358. translator evaluates B as if it appeared at the global scope. There's not much
  17359. to the extension itself, once you get the background out of the way.
  17360.  
  17361.  
  17362. Conversion from T **to const T *const *
  17363.  
  17364.  
  17365. As a general rule, C++ (like C) allows implicit pointer conversions that
  17366. increase const-ness, but not conversions that decrease const-ness. For
  17367. example, given
  17368. char *strcpy(char *s1, const char
  17369. *s2);
  17370. char a1[N], a2[N];
  17371. the call
  17372. strcpy(a1, a2);
  17373. successfully converts the expression a2 from type char[N] to char *, and then
  17374. to const char *. On the other hand, given
  17375. const char ca1[] = "asdf";
  17376. the call
  17377. strcpy(ca1, a2);
  17378. is an error because it requires converting ca1 from const char[5] to const
  17379. char * (OK so far), and then to char *. The last conversion is an error
  17380. because it tries to strip away a const qualifier. For that you would need a
  17381. cast.
  17382. Saying that C++ allows pointer conversions that increase const-ness is an
  17383. oversimplification. Actually, C++ (like C) forbids certain pointer conversions
  17384. that appear to increase const-ness. In particular,
  17385. T ** => const T **
  17386. is not safe, and C++ does not allow it. To see why this is unsafe, let's work
  17387. our way through a progression of similar conversions. In the following, =>
  17388. means "implicitly converts to."
  17389. It's true that for any type T,
  17390. T * => const T * //1a: OK
  17391. safely increases const-ness. This much is clear. You cannot use the result of
  17392. the conversion to corrupt a constant value. The const and T in //1a can appear
  17393. in either order, so it's also true that
  17394. T * => T const * //1b: OK
  17395. Now, if you replace T in //1b with U *, you get
  17396. U ** => U *const * //2: OK
  17397. Since //2 is okay, it appears that
  17398. T ** => const T ** //3: nope
  17399. should be also, but it isn't. If C++ allowed //3, then you could accidentally
  17400. change the value of a constant object, as shown by the following example.
  17401.  
  17402. C++ cannot accept
  17403. const char c= 'x';
  17404. char *pc = &c; //4: error
  17405. *pc = 'y'; //5: OK
  17406. because it would change the value of c, which is supposed to be const. The
  17407. error is not on //5; pc was declared to point to a non-const char. The error
  17408. is on //4, because it tries to convert
  17409. const char * => char * // no
  17410. which decreases const-ness.
  17411. Now, let's extend the example to:
  17412. const char c = 'x';
  17413. char *pc;
  17414. const char **ppc = &pc; // 6: ?
  17415. *ppc: &c; // 7: OK
  17416. *pc = 'y'; // 8: OK
  17417. This code also tries to change the value of c, but via a different route. The
  17418. problem is not on //7, which simply copies one const char* to another. Nor is
  17419. it on //8, which is the same as //5 in the previous fragment. No, the problem
  17420. is that //6 opens a hole in the const safety net. If C++ (or C) allowed
  17421. T ** => const T ** //3
  17422. then it would have to allow //6.
  17423. There is, however, a restricted conversion that is safe, namely,
  17424. T ** => const T *const* // 9: OK
  17425. This prevents the conversion on line //6 in the previous example. But is it
  17426. still useful? In his paper proposing this extension, Andrew Koenig of AT&T
  17427. showed that the conversion is useful with the following example.
  17428. Given an array such as
  17429. char *quintet[] =
  17430. {
  17431. "flute", "oboe", "horn", "clarinet",
  17432. "bassoon"
  17433. };
  17434. you can compute the length of the longest string in such an array with a
  17435. function declared as
  17436. size_t maxlen(char **a, size_t n);
  17437. Unfortunately, this function will not accept
  17438. const char *quartet[] =
  17439. {
  17440. "violin", "violin", "viola", "cello"
  17441. };
  17442. because const char ** does not convert to char **. Thus, it appears that you
  17443. must overload maxlen with another declaration:
  17444. size_t maxlen(const char **a, size_t n);
  17445. (You can't use this latter declaration in place of the original because
  17446. passing quartet as the first argument requires conversion //3 above, which we
  17447. already banished.)
  17448. However, with the extension to allow the conversion
  17449. T ** => const T *const *
  17450. you can get by with just one declaration for maxlen, namely,
  17451. size_t maxlen(const char *const *a, size_t n);
  17452. In fact, this one definition even accepts
  17453. const char * const trio[] =
  17454. {
  17455. "washtub", "jaw harp", "kazoo"
  17456. };
  17457. Listing 6 shows an implementation of this maxlen function that you can use for
  17458. experiments.
  17459. The committees actually approved a more general form of the conversion rule.
  17460. Here's what the draft says:
  17461. A conversion can add type qualifiers at levels other than the first in
  17462. multi-level pointers, subject to the following rules:
  17463. Two pointer types T1 and T2 are similar if there exists a type T and integer n
  17464. > 0 such that:
  17465. T1 is T cv1,n * . . . cv1,1 * cv1,0
  17466. and
  17467. T2 is T cv2,n * . . . cv2,1 * cv2,0
  17468. where each cvi,j is const, volatile, const volatile, or nothing.
  17469. An expression of type T1 can be converted to type T2 if and only if the
  17470. following conditions are satisfied:
  17471. the pointer types are similar.
  17472. for every j>0, if const is in CV1,j then const is in cv2j, and similarly for
  17473. volatile.
  17474. the cv1,j and cv2,j are different, then const is in every cv2,k for 0<k<j.
  17475. This means that the following conversions are also valid:
  17476. T *** => const T *const *const *
  17477. T **** => const T *const *const * const *
  17478.  
  17479. and so on. So are:
  17480. T *** => T **const *
  17481. T const **** => T const **const *const *
  17482. and even:
  17483. T** => T volatile *const volatile *
  17484. I'll describe more of these enhancements next month.
  17485. References
  17486. [1] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference
  17487. Manual (Addison-Wesley, 1990).
  17488. [2] Bjarne Stroustrup. The Design and Evolution of C++ Addison-Wesley, 1994).
  17489. [3] P. J. Plauger. The Standard C Library (Prentice Hall, 1992).
  17490.  
  17491. Listing 1 Using the dynamic_cast operator
  17492. class B { ... };
  17493. class D : public B { ... };
  17494.  
  17495. void f()
  17496. {
  17497. B *pb = new D;
  17498. ...
  17499. D *pd = dynamic_cast<D *>(pb); // a downcast
  17500. ...
  17501. pb = dynamic_cast<B *>(pd); // a "normal" upcast
  17502. }
  17503. // End of File
  17504.  
  17505.  
  17506. Listing 2 A nested class name hidden by a data member name
  17507. struct A
  17508. {
  17509. struct B
  17510. {
  17511. static int n;
  17512. } B;
  17513. enum E { e = 1 } E;
  17514. } A;
  17515.  
  17516.  
  17517. Listing 3 Dealing with long class names using typedefs
  17518. class VeryLongName
  17519. {
  17520. public:
  17521. void f();
  17522. ...
  17523. };
  17524.  
  17525. typedef VeryLongName VLN; // 1
  17526. class A : public VLN // 2
  17527. {
  17528. ...
  17529. };
  17530.  
  17531. void g(A *ap)
  17532. {
  17533. ap->VLN::f();
  17534. }
  17535. // End of File
  17536.  
  17537.  
  17538. Listing 4 A variation on the example in Listing 3
  17539.  
  17540. class VeryLongName
  17541. {
  17542. public:
  17543. void f();
  17544. ...
  17545. };
  17546.  
  17547. class A: public VeryLongName
  17548. {
  17549. ...
  17550. };
  17551.  
  17552. typedef VeryLongName VLN;
  17553.  
  17554. void g(A *ap)
  17555. {
  17556. ap->VLN::f();
  17557. }
  17558.  
  17559.  
  17560. Listing 5 Decoupling the class hierarchy structure from application code using
  17561. a member typedef
  17562. //
  17563. // class library code
  17564. //
  17565.  
  17566. class B
  17567. {
  17568. public:
  17569. void f();
  17570. ...
  17571. };
  17572.  
  17573. class D: public B
  17574. {
  17575. public:
  17576. typedef B inherited;
  17577. void f();
  17578. };
  17579.  
  17580. //
  17581. // library user's code
  17582. //
  17583.  
  17584. void g(D *pd)
  17585. {
  17586. pd->inherited::f();
  17587. ...
  17588. }
  17589. // End of File
  17590.  
  17591.  
  17592. Listing 6 A maxlen function intended for both arrays of constant strings and
  17593. arrays of non-constant strings
  17594. #include <iostream.h>
  17595. #include <string.h>
  17596.  
  17597. #define DIM(a) (sizeof(a)/sizeof(a[0]))
  17598.  
  17599. char *quintet[] =
  17600. { "flute", "oboe". "horn", "clarinet", "bassoon" };
  17601.  
  17602. const char *quartet[] =
  17603. { "violin", "violin", "viola", "cello" };
  17604. const char *const trio[] =
  17605. { "washtub", "jaw harp", "kazoo" };
  17606.  
  17607. size_t maxlen(const char *const *t, size_t n)
  17608. {
  17609. size_t len = 0;
  17610. size_t sl = 0;
  17611. size_t i;
  17612. for (i = 0; i < n; ++i)
  17613. if ((sl = strlen(t[i])) > len)
  17614. len = sl;
  17615. return len;
  17616. }
  17617.  
  17618. int main()
  17619. {
  17620. cout << maxlen(quintet, DIM(quintet)) << endl;
  17621. cout << maxlen(trio, DIM(trio)) << endl;
  17622. return 0;
  17623. }
  17624. /* End of File */
  17625.  
  17626.  
  17627.  
  17628.  
  17629.  
  17630.  
  17631.  
  17632.  
  17633.  
  17634.  
  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. Questions & Answers
  17666.  
  17667.  
  17668. Moving On
  17669.  
  17670.  
  17671.  
  17672.  
  17673. Kenneth Pugh
  17674.  
  17675.  
  17676. Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++
  17677. language courses for corporations. He is the author of All On C, C for COBOL
  17678. Programmers, and UNIX for MS-DOS Users, and was a member of the ANSI C
  17679. committee. He also does custom C/C++ programming and provides
  17680. SystemArchitectonicssm services. He is president of the Independent Computer
  17681. of the Independent Computer Consultants Association. His address is 4201
  17682. University Dr., Suite 102, Durham, NC 27707. You may fax questions for Ken to
  17683. (919) 489-5239. Ken also receives email at kpugh@allen.com (Internet) and on
  17684. Compuserve 701215,1142.
  17685.  
  17686.  
  17687. This will be my last Q & A column for the C/C++ Users Journal. My cat,
  17688. Nameless, who sat on my lap while I typed my four books and many of these
  17689. columns, died in November. I dedicate this last column to her.
  17690. It's been an interesting time to write for a programming magazine. I created
  17691. my first columns on an Osborne with an 8088, running Wordstar on CP/M. My last
  17692. columns were written on a Thinkpad with a 486, running Wordstar in an MS-DOS
  17693. window in Microsoft Windows. You can't teach an old dog new editing tricks.
  17694. During the early days of this column, the ANSI C committee was established and
  17695. the draft was approved as a standard The ANSI C++ committee started during the
  17696. colunm's later days and its draft may be issued for voting soon.
  17697. The C compilers used on the Osborne fit onto two floppy disks. Kernighan and
  17698. Ritchie explained the entire language in a small book. The latest C++
  17699. compilers I use on my Dell Pentium come on CD/ROM and take almost 100 MB of
  17700. hard disk. The language and the interactions between its features have grown
  17701. tremendously. For example, contemplating the ramifications of exception
  17702. handling within a template that uses multiple inheritance from templated
  17703. classes which themselves contain exception handlers is not a light task.
  17704. I relinquish my column because I cannot move close to the speed of light and
  17705. therefore cannot stretch out my ever shrinking time to continue answering your
  17706. questions -- good questions that deserve attention I can no longer give. With
  17707. the continuing spread of object-oriented programming and client/server
  17708. technology, my teaching obligations have increased enormously. COBOL
  17709. programmers are switching to C and C++ in droves. Demand for skills in UNIX,
  17710. used for database servers, has dramatically grown. One publisher has been
  17711. begging me for the last year for a book on the distributed computing
  17712. environment. Another one wants a "Best of Questions and Answers."
  17713. In April, I assume the presidency of the Independent Computer Consultant's
  17714. Association. The ICCA is blossoming as corporations downsize, but still
  17715. require the talent to produce their products. The anticipated growth of the
  17716. association may engulf much of my remaining free time.
  17717. In addition to time constraints, I have been slowly transforming my practice
  17718. to what I call System Architectonics. Incorporating object-oriented analysis
  17719. and design, its emphasis is on the interface to objects. Part of System
  17720. Architectonics is the specification of that interface in a manner
  17721. understandable to both the object designer and the object user. SA moves away
  17722. from the actual coding in C/C++ to the abstraction of the design.
  17723. Although I will not appear physically in these pages any more, I will still be
  17724. present in the electronic world at 70125.1142@compuserve.com and eventually at
  17725. kpugh@pughkilleen.com, which is currently being established.
  17726. I wish my successor the best of luck in his column.
  17727. Good-bye to all you C and C++ fans. See you in cyberspace.
  17728.  
  17729.  
  17730.  
  17731.  
  17732.  
  17733.  
  17734.  
  17735.  
  17736.  
  17737.  
  17738.  
  17739.  
  17740.  
  17741.  
  17742.  
  17743.  
  17744.  
  17745.  
  17746.  
  17747.  
  17748.  
  17749.  
  17750.  
  17751.  
  17752.  
  17753.  
  17754.  
  17755.  
  17756.  
  17757.  
  17758.  
  17759.  
  17760.  
  17761.  
  17762.  
  17763.  
  17764.  
  17765. CUG New Releases
  17766.  
  17767.  
  17768. Portable TAR, ED, LPC, and Two Updates
  17769.  
  17770.  
  17771.  
  17772.  
  17773. Victor R. Volkman
  17774.  
  17775.  
  17776. Victor R. Volkman received a BS in Computer Science from Michigan
  17777. Technological University. He has been a frequent contributor to C/C++ Users
  17778. Journal since 1987. He is currently employed as Senior Analyst at H.C.I.A. of
  17779. Ann Arbor, Michigan. He can be reached by dial-in at the HAL 9000 BBS (313)
  17780. 663-4173 or by Usenet mail to sysop@hal9k.com.
  17781.  
  17782.  
  17783.  
  17784.  
  17785. Update to CUG #391: C/C++ Exploration Tools
  17786.  
  17787.  
  17788. C/C++ Exploration Tools, by Juergen Mueller (Kornwestheim, Germany), includes
  17789. both his C Function Tree Generator (CFT) and the C Structure Tree Generator
  17790. (CST). CFT and CST analyze C/C++ source code, from applications of any size,
  17791. contained in multiple files. CFT and CST are useful for exploring new, unknown
  17792. software and for supporting reuse, maintenance, and re-engineering. By
  17793. preprocessing, scanning, and analysing the program source code, these programs
  17794. generate the function call hierarchy (CFT) and the data structure/class (CST)
  17795. relations. Both programs can handle C and C++ code; CFT can additionally
  17796. analyze assembler code. The C Exploration Tools v2.20, as MS-DOS executables
  17797. (released 03/20/94), are immediately available as CUG volume #391.
  17798. Version 2.20 of C Exploration Tools features significant enhancements over the
  17799. previously released version 2.12. Here are just a few of the changes that
  17800. Mueller reports:
  17801. Microsoft Visual C++ 1.0 for Windows NT preprocessing support
  17802. Borland C++ 1.0 for OS/2 preprocessing support
  17803. A fix for CFT/CST database read errors that occured if line numbers were not
  17804. in strictly ascending order inside functions or structures (a possibility if
  17805. #line directives are used)
  17806. A fix for CFT errors that occured with nested C++ constructs such as extern
  17807. "C" { ... } (this was sometimes the reason for the error message 'unbalanced
  17808. braces')
  17809. Detection and display of multiple file inclusions (of the same include file),
  17810. with scanned source size and number of lines calculated for multiple
  17811. inclusions
  17812. Support for the _TlMESTAMP_macro in preprocessors
  17813. Option -Jcharset, which adds an extended character set for identifier
  17814. recognition and allows the use of national character sets
  17815. Option -//, which accepts C++ comments in C source code to ensure
  17816. compatibility with Microsoft and Borland C compilers
  17817. Option -RATIONAL, which generates a Rational Rose Petal file for callgraph
  17818. visualisation (see documentation for detailed description!)
  17819. A change to option -I that makes -I* ignore missing include files during
  17820. preprocessing
  17821. A new option -o[name] for CFTN and CSTN, which prints output to file 'name'
  17822. Different return values for CFTN and CSTN to avoid conflicts with DOS errors
  17823. (affects BRIEF macros)
  17824.  
  17825.  
  17826. Update to CUG #363: MC68020 Cross-Assembler
  17827.  
  17828.  
  17829. Andrew E. Romer (Worthing, West Sussex, England) updates his Motorola MC68020
  17830. Cross-Assembler. This update completely replaces the earlier version of his
  17831. work cataloged as CUG #363. Romer's MC68020 assembler was originally based on
  17832. Paul McKee's MC68000 assembler (North Carolina State University). The latest
  17833. version of Romer's assembler fixes a bug causing incorrect generation of S2
  17834. records. Thanks to Eric M. Scharf (University of London, EE Dept.) for
  17835. reporting it.
  17836. A cross-assembler is an assembly language translator that runs on a different
  17837. platform than the one it is assembling for. In this case, the MC68020
  17838. cross-assembler runs on an MS-DOS machine (80x86 CPU) but reads assembly
  17839. language for the MC68020 microprocessor and writes MC68020 object files.
  17840. Typically, the developer then downloads these object files to the target
  17841. machine (presumably a Macintosh, Atari, Amiga, or other MC68020 architecture
  17842. machine).
  17843. The cross-assembler supports constants in hexadecimal, binary, octal, and
  17844. decimal. Constant expressions can use many C-style operators including bitwise
  17845. operators. The following assembler directives are supported: END, ORG, EQU,
  17846. SET, REG, DC (define constant), DCB (define constant block), and DS (define
  17847. storage). The cross-assembler lacks support for native math coprocessor
  17848. instructions, string constants, and macros. Documentation consists of a
  17849. 12-page ASCII reference manual.
  17850. The MC68020 cross-assembler for MS-DOS will compile under either Microsoft C
  17851. or Zortech C. Version 1.01 (as released 03/04/94) is immediately available as
  17852. CUG volume #363.
  17853.  
  17854.  
  17855. New Acquisitions
  17856.  
  17857.  
  17858. ED Editor (CUG #424): Multiplatform full screen text editor with windowing,
  17859. regular expressions, and programming support features. Edits in ASCII, binary,
  17860. or hexadecimal modes
  17861. Portable TAR and LZPIPE (CUG #425): Multiplatform archiving tools that handle
  17862. files, floppies, and QIC streamer tapes in tar, zip, gzip, or compress formats
  17863. LPC-Parcor-Cepstrum code generation for C (CUG #426): Comprehensive libraries
  17864. for audio data file normalization and manipulation
  17865.  
  17866.  
  17867. CUG 424: ED Editor
  17868.  
  17869.  
  17870. Charles Sandmann (Houston, TX) submits the ED editor with a user interface
  17871. based on the DEC VMS EDT editor. ED is a true multiplatform editor and can be
  17872. compiled and run on virtually any platform. It includes target-specific code
  17873. for keyboard, screen, and TCP/IP handling. This design allows ED to run in
  17874. UNIX (IBM RS/6000, Sun Sparc, HP, NeXT, or Alpha AXP machines), MS-DOS,
  17875. Windows NT, and OS/2 environments with ease. ED can edit any kind of file in
  17876. text, binary, or hexadecimal modes.
  17877. Some of ED's more interesting features include the following:
  17878.  
  17879. Multiple text windows
  17880. Built-in file manager
  17881. Editing by wildcards
  17882. Calculator
  17883. Automatic program indentation
  17884. Parenthesis matching
  17885. Box and columnar editing
  17886. Insert and overstrike editing
  17887. Sorting
  17888. Load/save of files using FTP
  17889. The ED documentation consists primarily of a 45-page ASCII help file. You can
  17890. make this help file from within ED or using any standard text utilities you
  17891. might have. The documentation assumes that you've had some exposure to the EDT
  17892. editor that ED emulates or that you are willing to learn the basics.
  17893. The CUG Library distribution of ED includes binaries built with the DJGPP
  17894. edition of GNU C/C++ (MS-DOS with GO32 DOS extender). Also, this two-diskette
  17895. set provides the full C source. Distribution and use of the ED source code is
  17896. covered by the GNU General Public License (Version 2). ED version 1.5.7 (as
  17897. released on 04/05/94) is immediately available as CUG #424.
  17898.  
  17899.  
  17900. CUG #425A: Portable TAR
  17901.  
  17902.  
  17903. Timor V. Shaporev (Moscow, Russia) contributes an extremely versatile version
  17904. of the classic UNIX TAR archiver and an innovative method of delivering LZW
  17905. compressed data over pipes. Portable TAR works with both MS-DOS and
  17906. UNIX-compatible machines. Since more than half the source code available from
  17907. the Internet appears in TAR format, you'll quickly find this a valuable
  17908. utility. portable TAR reads and writes archives in ordinary files, raw
  17909. floppies, and QIC-02 streamer tapes. It understands regular TAR formats,
  17910. PKZIP, gzip, and UNIX compress.
  17911. Portable TAR has several other advantages over most public domain TAR programs
  17912. and those included with UNIX-compatible operating systems:
  17913. Uniform processing across both MS-DOS and UNIX platforms
  17914. Reading/writing of UNIX-compatible floppies and quarter-inch streamer
  17915. cartriges under DOS
  17916. Support for unusual floppy formats: 80-tracks-by-9-sectors and DEC Rainbow
  17917. (under DOS)
  17918. A data compression option under both DOS and UNIX
  17919. System V and/or GNU multivolume archive read capability under DOS and all UNIX
  17920. clones
  17921. An option to restore damaged archives (and plenty of other useful options)
  17922. As mentioned earlier, Shaporev claims source compatibility with most UNIX
  17923. systems and MS-DOS. Specifically, he provides two makefiles that cover most
  17924. UNIX implementations and another makefile for Borland Turbo C in MS-DOS. As
  17925. you might expect, a small amount of assembly language code is provided for
  17926. supporting functionality not normally found in MS-DOS.
  17927. The CUG Library distribution of Portable TAR includes binaries built for
  17928. MS-DOS. Portable TAR version 3.15 (as released on 04/05/94) is immediately
  17929. available on CUG #425.
  17930.  
  17931.  
  17932. CUG #425B: LZPIPE
  17933.  
  17934.  
  17935. Shaporev's other contribution is the LZPIPE library, which implements the two
  17936. most popular compression methods, LZW and deflate. Both of these methods are
  17937. defacto lossless compression standards. LZW is used in the well-known compress
  17938. utility and deflate is used by a number of others, starting from PKZIP by
  17939. PKWare Inc. up to GNU GZIP.
  17940. LZPIPE provides to systems like MS-DOS a programming capability analogous to
  17941. UNIX pipes. It also allows access to compressed files, and provides a far
  17942. simpler API than most compression utilities. Specifically, this library
  17943. processes compressed data in the familiar file handle style of open, read,
  17944. write, and close calls.
  17945. LZIPE implements only pure compression -- no attempt is made to emulate ZIP
  17946. directory services. Thus, you would either use LZPIPE to compress one file at
  17947. a time or else add the extra functionality for multi-file archiving yourself.
  17948. Source codes for LZW compression and decompression are derived from sources of
  17949. the compress utility initially written by Joe Orost. Source codes for deflate
  17950. compression and inflate decompression are derived from Info-Zip zip/unzip
  17951. utilities sources. Inflate was initially written by Mark Adler and deflate
  17952. originated with Jean-loup Gailly.
  17953. The CUG Library distribution of LZIPE includes only C source code. As this is
  17954. strictly a library, no MS-DOS binaries are included. LZPIPE version 1.01 (as
  17955. released 04/05/94) is immediately available as part of CUG #425.
  17956.  
  17957.  
  17958. CUG #426: LPC-Parcor-Cepstrum Code Generator
  17959.  
  17960.  
  17961. Patrick Ko Shu Pui (Hong Kong) submits his LPC-Parcor-Cepstrum code generator
  17962. for C. The LPC-Parcor-Cepstrum code generator (hereafter, LPC) will compile on
  17963. most UNIX platforms as well as under Microsoft C/C++ 7.0 and Borland Turbo C
  17964. v2.0. This archive's primary use is the manipulation and normalization of
  17965. audio data files. Specifically, LPC supports eight-bit ulaw (SUN Sparc),
  17966. eight-bit and 16-bit PCM data. LPC then generates LPC autocorrelation or
  17967. covariance coefficients, Parcor (partial correlation) coefficients, or LPC
  17968. cepstrum coefficients.
  17969. LPC's implementation draws from algorithms and methods described by Shuzo
  17970. Saito and Kazuo Nakata in Fundamentals of Speech Signal Processing (1985) and
  17971. others.
  17972. Astute CUJ readers will recall that Patrick Ko also contributed the Small
  17973. Matrix Toolbox (CUG #403) earlier this year. In fact, the LPC application
  17974. includes several key components from the Small Matrix Toolbox.
  17975. The C source package included in LPC is free for academic purposes only. For
  17976. commercial usage, you must send a US $30 money order addressed to the author
  17977. (Patrick Ko). The CUG Library distribution includes full C source and binaries
  17978. for MS-DOS. LPC version 0.52 (as released 04/16/94) is immediately available
  17979. as CUG #426.
  17980.  
  17981.  
  17982.  
  17983.  
  17984.  
  17985.  
  17986.  
  17987.  
  17988.  
  17989.  
  17990.  
  17991.  
  17992.  
  17993.  
  17994.  
  17995.  
  17996.  
  17997. Editor's Forum
  17998. One of the side effects of publishing a magazine on C and C++ is that people
  17999. ask us a lot of questions. I personally get e-mail several times a week, on
  18000. average, from people seeking help. Sometimes it's questions about standards
  18001. conformance, sometimes about how to use some esoteric language feature. The
  18002. harder ones to answer are those asking for recommendations on what compiler to
  18003. buy, or peculiar features of some specific implementation. The really tough
  18004. ones are questions about debugging some lengthy sequence of code.
  18005. I answer all of my e-mail, even though that commitment often costs me an hour
  18006. or more a day. The answers probably aren't always satisfactory -- I refuse,
  18007. for example, to comment on the relative merits of commercial products. And the
  18008. answers sometimes go astray. My DOS-based mailer can get bollixed by a long or
  18009. esoteric return address. If you've written me in the past few years and
  18010. received no reply to a direct question, know that I almost certainly tried to
  18011. send you an answer.
  18012. A more reliable way to get your questions answered is to send e-mail straight
  18013. to R&D Publications. The folks who publish this magazine have better resources
  18014. for tracking mail, and for finding someone who can answer it more or less
  18015. wisely. For example, they know to pass Windows queries on to almost anybody
  18016. else but me -- I still enjoy relative ignorance of that particular morass.
  18017. One reliable source of answers lo these many years has been Ken Pugh. His
  18018. Q?/A! column has fearlessly tackled a wide range of questions, usually with
  18019. enlightening results. Often, Ken's explanations have stimulated even more
  18020. detailed supplemental contributions from other readers. It is no surprise that
  18021. Q?/A! consistently garners positive comments from our letter writers.
  18022. Thus, I report with some sadness that Ken Pugh is retiring from his post as
  18023. resident CUJ expert on almost everything. For some of the reasons why, you can
  18024. read his farewell address later in this issue. My job is to thank him, on
  18025. behalf of all you readers, for the yeoman effort he has put in for so long. He
  18026. will be missed.
  18027. That's not to say that we've run out of answers. CUJ continues to welcome
  18028. questions, particularly those whose answers can help other readers as well.
  18029. (Don't hesitate to ask because you think the question is too trivial --
  18030. someone else is probably waiting for you to get the courage to ask it
  18031. instead.) Keep those cards and letters rolling in, and we'll keep supplying
  18032. folks who are willing to answer them patiently.
  18033. P.J. Plauger
  18034. pjp@plauger.com
  18035.  
  18036.  
  18037.  
  18038.  
  18039.  
  18040.  
  18041.  
  18042.  
  18043.  
  18044.  
  18045.  
  18046.  
  18047.  
  18048.  
  18049.  
  18050.  
  18051.  
  18052.  
  18053.  
  18054.  
  18055.  
  18056.  
  18057.  
  18058.  
  18059.  
  18060.  
  18061.  
  18062.  
  18063.  
  18064.  
  18065.  
  18066.  
  18067.  
  18068.  
  18069.  
  18070.  
  18071.  
  18072.  
  18073.  
  18074.  
  18075.  
  18076.  
  18077.  
  18078.  
  18079.  
  18080.  
  18081.  
  18082.  
  18083.  
  18084.  
  18085.  
  18086.  
  18087. New Products
  18088.  
  18089.  
  18090. Industry-Related News & Announcements
  18091.  
  18092.  
  18093.  
  18094.  
  18095. Liant C++/Views Supports Macintosh, SGI, Windows NT, and DEC Alpha OSF/1
  18096.  
  18097.  
  18098. Liant Software has ported C++/Views, an application framework for developing
  18099. multi-platform, native GUI applications in C++ to several additional
  18100. platforms. C++/Views lets portable GUI applications be developed, and then
  18101. deployed across multiple GUI platforms, without code modification. The new
  18102. platforms supported by C++/Views include Macintosh, SGI, Windows NT, and DEC
  18103. Alpha OSF/1. C++/Views also supports OSF/Motif (AIX, HP-UX, Solaris, and SUN
  18104. OS), OS/2, and Windows.
  18105. With C++/Views, developers can create one set of source code and resources for
  18106. their applications, regardless of the number of platforms on which they want
  18107. it to run. When compiled on each platform, the application will adhere to the
  18108. native look and feel of that platform. Because the dimension of the display
  18109. and user interface components on systems vary, C++/Views uses the Geometry
  18110. Manager, a mechanism to preserve spatial relationships. The Geometry Manager
  18111. automatically manages the size and portion of interface components within
  18112. dialogs, letting the developer create an element and later resize it.
  18113. C++/Views includes a library of 100 C++ classes including interface design,
  18114. data management, event processing, and several higher-level interface classes
  18115. including table, toolbar, and MDI classes. C++/Views also includes a visual
  18116. development tool, C++/Views Constructor. C++/Views Constructor combines a
  18117. class browser with a visual interface builder, which lets the user draw and
  18118. test an interface, and then switch to editing the C++ source code within the
  18119. same environment.
  18120. The Windows/Windows NT, Macintosh, and OS/2 version of C++/Views are priced at
  18121. $1,499. The SGI, DEC Alpha OSF/1, and other OSF/Motif versions are priced at
  18122. $2,499. For more information contact Liant Software, Framingham, MA; (508)
  18123. 872-8700.
  18124.  
  18125.  
  18126. Tartan Acquires Energize and Lucid C/C++ Compilers
  18127.  
  18128.  
  18129. Tartan Inc. has acquired the technology for Energize, the object-oriented
  18130. development environment, and the C/C++ compilers from Lucid Inc. The Energize
  18131. Programming System is a C/C++ development environment, which supports the code
  18132. construction and maintenance phases of application development by providing an
  18133. integrated, incremental development process. Lucid C is a production-quality,
  18134. dual-mode compiler supporting ANSI C and K&R C. Lucid C++ is a C++ compiler
  18135. that provides five modes of operation, including cfront compatibility and
  18136. direct compilation of C++ code. The Lucid C capabilities are included in Lucid
  18137. C++. Lucid C/C++ compilers operate on Sun SPARC and SPARC-compatible machines,
  18138. running SunOS 4.1.x or Solaris 2.x.
  18139. Tartan is offering Lucid customers a transition plan for maintaining existing
  18140. investments without disruption. Current Lucid customers will be contacted
  18141. concerning the specifics of the transition plan. Tartan is also organizing a
  18142. new business unit to further develop, market, and support the Energize and
  18143. C/C++ products.
  18144. For more information contact Tartan, Inc., 300 Oxford Dr., Monroeville, PA
  18145. 15146; (412) 856-3600; FAX: (412) 856-3636.
  18146.  
  18147.  
  18148. MetaWare Announces High C/C++ Compiler with DirectToSOM C++ for OS/2
  18149.  
  18150.  
  18151. MetaWare has announced the High C/C++ compiler with DirectToSOM C++ for OS/2.
  18152. With DirectToSOM, C/C++ source code can be compiled with High C/C++ to create
  18153. SOM-compatible binaries without writing Interface Definition Language (IDL)
  18154. class descriptions. SOMobjects compiled with High C/C++ for OS/2 can
  18155. interoperate with SOMobjects written in other languages.
  18156. IBM's System Object Model (SOM) technology was developed to let programmers
  18157. upgrade binary C++ libraries without having to recompile client source code.
  18158. High C/C++ compilers include C++ exception handling (including nested
  18159. exceptions), namespaces to avoid name clashes with third-party libraries,
  18160. run-time type information (RTTI), and ANSI C++ casting notation to make C++
  18161. typecasts safer than Standard C notation. High C/C++ also provides OMF common
  18162. areas for templates, virtual function tables, RTTI, and inline functions. High
  18163. C/C++ also includes "thread-safe" libraries that can be used in multi-threaded
  18164. applications. All library functions are documented as to whether or not they
  18165. support reentracy. In addition, High C/C++ includes switches for "fine-tuning"
  18166. function inlining, optimizations to produce executables for specific
  18167. processors such as the Pentium, and support for long long types.
  18168. The High C/C++ DirectToSOMC++ for OS/2 compiler is priced at $595. For more
  18169. information contact MetaWare Incorporated, 2161 Delaware Ave., Santa Cruz, CA
  18170. 95060-5706; (408) 429-6382; FAX: (408) 429-9273; E-mail:
  18171. techsales@metaware.com.
  18172.  
  18173.  
  18174. ACI Ships Object Master for Windows
  18175.  
  18176.  
  18177. ACI US, Inc. has begun shipping Object Master for Windows, an integrated,
  18178. cross-platform programming tool for writing and organizing source code. Object
  18179. Master for Windows lets C/C++ programmers write, edit, organize, and navigate
  18180. through source code while using drag-and-drop Windows functions. Object Master
  18181. integrates with Microsoft Visual C++, Symantec C++, Borland C/C++, and DOS
  18182. compilers. Object Master can trigger compilations and receive errors without
  18183. switching to the compiler environments.
  18184. Object Master includes a source code editor, a project window, and a browser
  18185. that lets users edit select pieces of code. In addition, Object Master has a
  18186. Class Tree Window. Object-oriented programmers can view different parts of the
  18187. Class Tree simultaneously by opening multiple windows.
  18188. Object Master for Windows also parses each file as it is added to a project
  18189. and includes it in a data dictionary. The data dictionary lists definitions
  18190. and locations of named programming components such as classes, functions,
  18191. fields, and data types. Once parsed, the information is automatically updated
  18192. in the browser and editing windows. With a single keystroke, programmers can
  18193. use template calls generated by Object Master to insert method or function
  18194. arguments. In addition, Object Master color-codes and stylizes text for
  18195. identification. Programmers can customize styles for each language element
  18196. including functions, keywords, compiler directives, syntax errors, disable
  18197. directives, and comments. Object Master also checks a file's syntax and lists
  18198. errors in a window.
  18199. Object Master for Windows is priced at $249. For more information contact ACI
  18200. US, Inc., 20883 Stevens Creek Blvd., Cupertino, CA 95014; (408) 252-4444; FAX:
  18201. (408) 252-4829; AppleLink: D4444.
  18202.  
  18203.  
  18204. Popkin Software Announces OMT Support for System Architect
  18205.  
  18206.  
  18207. Popkin Software & Systems, Inc. has announced that System Architect supports
  18208. the OMT/Rumbaugh method of object-oriented analysis and design. System
  18209. Architect supports integrated forward and reverse engineering for C++ code
  18210. generation from designs created using the OMT/Rumbaugh methodology. Both
  18211. skeleton files and C++ headers for an object-oriented application are
  18212. automatically generated.
  18213. Optional modules available for System Architect 3.0 include SA
  18214. Object-Oriented, SA Schema Generator, SA Reverse Data Engineer, SA Screen
  18215. Painter, SA Project Documentation Facility, SA PowerBuilder/Link, and SA/SQL
  18216. Windows Link.
  18217. In a related announcement, Popkin will ship upgraded support for
  18218. object-oriented analysis and design techniques from Grady Booch and Peter
  18219. Coad/Ed Yourdon to include changes in the most recent version. The upgraded
  18220. support also includes the generation of skeleton files and C++ headers.
  18221. Prices for System Architect 3.0 for Microsoft Windows range from $1,395 to
  18222. $2,940. Prices for System Architect 3.0 for 0S/2 PM range from $1,795 to
  18223. $3,790. Optional modules range in price from $495 to $1,995. Users of SA
  18224. Object-Oriented with an active maintenance agreement will receive the
  18225. OMT/Rumbaugh addition and the upgrades to Booch & Coad/Yourdon without charge.
  18226. For more information contact Popkin Software & Systems, Inc., 11 Park Place,
  18227. New York, NY, 10007-2801; (212) 571-3434; FAX: (212) 571-3436.
  18228.  
  18229.  
  18230. Amzi! Upgrades Cogent Prolog
  18231.  
  18232.  
  18233. Amzi! inc. has upgraded Cogent Prolog. Cogent Prolog is an Edinburgh-standard
  18234. (non-typed) Prolog development system including a compiler, interpreter,
  18235. interactive development environment, debugger, and royalty-free, distributable
  18236. run-time module. The Prolog source and object code is portable across
  18237. platforms. Cogent Prolog v3.0 includes a Logic Server API, which lets Windows,
  18238. DOS, and Alpha programmers integrate Prolog's symbolic reasoning with
  18239. conventional C/C++ applications.
  18240. The Cogent Prolog Logic Server API provides 50 functions for interacting with
  18241. Prolog code and data, and for extending the Cogent runtime to directly access
  18242. GUI tool kits, database APIs, multimedia, or other facilities available to the
  18243. C/C++ programmer. The API is available as a Windows DLL for use by a variety
  18244. of C/C++ systems, as well as Visual Basic and Access; 16- and 32-bit,
  18245. static-link libraries for use with Windows and DOS C/C++ tools; a C++ wrapper
  18246. that makes the Logic Server an object; and a 64-bit DEC Alpha OSF/1 C/C++
  18247. library.
  18248.  
  18249. Cogent Prolog v3.0 is priced at $298 for DOS and Windows and $995 for OSF/1.
  18250. Subsets of the full system are also available starting with the student
  18251. interpreter for $49. For more information contact Amzi! inc., 40 Samuel
  18252. Prescott Dr., Stow, MA 01775; (508) 897-7332; FAX: (508) 897-2784; Internet:
  18253. amzi@world.std.com.
  18254.  
  18255.  
  18256. Performix Announces Drag-It 2.0 and Drag-It/VBX
  18257.  
  18258.  
  18259. Performix has announced two software add-on products: Drag-it v2.0 for Visual
  18260. C++ and Drag-it/VBX for Visual Basic. Drag-it v2.0 includes a class library,
  18261. starter files, "Drag-it Builder," and a sample application. Drag-it v2.0
  18262. includes OLE support so that programmers may create applications in which
  18263. symbols can be dragged from one Drag-it window to another. Drag-it diagrams or
  18264. symbols can also be embedded into other Microsoft OLE containers such as
  18265. Microsoft Word.
  18266. The Drag-it class library extends the Microsoft foundation classes, and the
  18267. Drag-it toolkit incorporates Drag-it interfaces into applications. Starter
  18268. files assist the programmer in creating a skeleton application. Drag-it
  18269. Builder is used to drag symbols and place them on a palette, or to import and
  18270. place bitmaps there also. A sample application is included that shows both how
  18271. to interpret Drag-it actions, and how to add a view to supplement information
  18272. shown graphically by Drag-it.
  18273. Drag-it/VBX provides Visual Basic programmers with a drag and drop front-end
  18274. to their applications. Using Drag-it/VBX, programmers can create a Drag-it
  18275. palette with symbols, or use Drag-it Builder to import bitmaps or draw
  18276. symbols, or be notified of user actions by Drag-it/VBX Events. Also included
  18277. in Drag-it/VBX is a sample application.
  18278. Drag-it v2.0 is priced at $495. Drag-it/VBX is priced at $295. For more
  18279. information contact Performix, 6618 Daryn Dr., Westhills, CA 91307; (800)
  18280. 337-2448 or (818) 992-0840; FAX: (818) 347-9455.
  18281.  
  18282.  
  18283. Inmark Ships zApp Developer's Suite for X/Motif
  18284.  
  18285.  
  18286. Inmark Development Corporation has begun shipping the zApp Developer's Suite
  18287. for X/Motif. The zApp Developer's Suite consists of the zApp Application
  18288. Framework, the zApp Interface Pack, and zApp Factory. The zApp Application
  18289. Framework is a set of C++ foundation classes. The zApp Interface Pack is a set
  18290. of custom controls and high-level visual objects. The zApp Factory is a
  18291. drag-and-drop application designer, code generator, and design testing
  18292. facility for building user interfaces.
  18293. The zApp Developer's Suite for X/Motif is available on Solaris 2, SunOS, IBM
  18294. AIX, HP-UX, SCO UNIX, SGI IRIX, Novel UNIXware, and Sun Solaris x86. The zApp
  18295. Developer's Suite for X/Motif is priced at $2,995 per developer. Source code
  18296. for zApp and the zApp Interface Pack are included at no additional charge. For
  18297. more information contact Inmark Development Corporation, 2065 Landings Dr.,
  18298. Mountain View, CA 94043; (415) 691-9000; FAX: (415) 691-9099; E-mail:
  18299. info@inmark.com.
  18300.  
  18301.  
  18302. MultiQuest Releases S-CASE 2.0
  18303.  
  18304.  
  18305. MultiQuest Corporation has released S-CASE 2.0. S-CASE uses the Booch notation
  18306. to graphically illustrate and model software systems. C++ code is then
  18307. generated automatically from the model. Features of S-CASE 2.0 include
  18308. iterative C++ code generation, control over method and attribute order, #ifdef
  18309. and macro support, class specification reports, printing of an entire model or
  18310. category, printing from the integrated text editor, and a menu layout. Other
  18311. features of S-CASE 2.0 include Booch's 1994 notation, real-time rule checking,
  18312. C++ code generation, hierarchical project manager, heterogeneous multi-user
  18313. support, and full color support.
  18314. Platforms supported by S-CASE 2.0 include Microsoft Windows, Macintosh, Sun,
  18315. and HP9000. S-CASE stores data in a platform-independent format allowing
  18316. several users to work simultaneously on the same design from any supported
  18317. platform.
  18318. Prices for S-CASE 2.0 range from $495 to $995 depending on configuration.
  18319. Floating licenses are also available. For more information contact MultiQuest
  18320. Corporation, 1699 E. Woodfield Rd., Suite A-1, Schaumburg, IL 60173; (708)
  18321. 240-5555; FAX: (708) 240-5556; E-mail: 72531.2510 @ compuserve.com.
  18322.  
  18323.  
  18324. DataFocus Ships NuTCRACKER X/SDK
  18325.  
  18326.  
  18327. DataFocus Incorporated has begun shipping NUTCRACKER X/Software Development
  18328. Kit (X/SDK), a member of the NuTCRACKER family of UNIX-to-Win32 porting tools.
  18329. NuTCRACKER X/SDK lets X and Motif-based GUI applications move to Windows NT.
  18330. Rather than emulate UNIX on Win32, developers using NuTCRACKER X/SDK recompile
  18331. their UNIX and X/Motif source code and link it to NuTCRACKER's DLLs, resulting
  18332. in native Win32 applications. NuTCRACKER, which includes the MKS Toolkit
  18333. utilities and a collection of UNIX calls based on SVR4, Berkeley, and POSIX,
  18334. supports UNIX applications written in C/C++.
  18335. For more information contact DataFocus Incorporated, 12450 Fair Lakes Circle,
  18336. Suite 400, Fairfax, VA 22033-3831; (800) 637-8034 or (703) 631-6770; FAX:
  18337. (703) 818-1532; Internet: nutcracker@datafocus.com.
  18338.  
  18339.  
  18340. Blinkinc Releases Blinker 3.1
  18341.  
  18342.  
  18343. Blinkinc has released Blinker 3.1, a royalty-free DOS extender, Windows and
  18344. OS/2 linker, and DOS dynamic overlay linker, which offers incremental linking
  18345. for protected-mode CA-Clipper programs. Features of Blinker 3.1 include
  18346. protected-mode support for Microsoft C/C++ and FORTRAN graphics libraries,
  18347. creation of Windows DLLs, dual mode support with internal or external overlay
  18348. files, OS/2 support, and double the link-time virtual memory capacity for
  18349. larger libraries and .EXE files.
  18350. Blinker 3.1 requires a minimum of an 8086 microprocessor to link or run
  18351. real-mode programs, while the DOS extender requires a minimum of an 80286
  18352. microprocessor to run protected-mode programs, and is compatible with the
  18353. DPMI, VCPI, and XMS specifications. Protected-mode programs created by Blinker
  18354. 3.1 will run under Windows, OS/2, and DOS.
  18355. Available for registered users to download from Blinkinc's BBS and CompuServe,
  18356. Blinker 3.1 is also being sent on disk at no extra charge to subscribers of
  18357. the Blinkinc Newsletter and Interim Upgrade Service. For more information
  18358. contact Blinkinc, 8001 W. Broad St., Richmond, VA 23294; (804) 747-6700; FAX:
  18359. (804) 747-4200; BBS: (804) 747-7333.
  18360.  
  18361.  
  18362. Trinzic Announces Rational Rose/ObjectPro
  18363.  
  18364.  
  18365. Trinzic Corporation and Rational Software Corporation have announced Rational
  18366. Rose/ObjectPro, a graphical, object-oriented tool for analysis and design of
  18367. client/server applications. Rational Rose/ObjectPro automates the production
  18368. of ObjectPro code. ObjectPro is Trinzic's object-oriented development
  18369. environment that combines C++ and Smalltalk.
  18370. Using information in the Rational Rose/ObjectPro class diagrams, developers
  18371. can generate ObjectPro source code files. These files contain ObjectPro code
  18372. constructs that correspond to the notation items, classes, relationships, and
  18373. adornments that have been defined in the model with Rose diagrams and
  18374. specifications. Rational Rose/ObjectPro generates ObjectPro classes, instance,
  18375. attribute, and method structures that define an ObjectPro application.
  18376. Rational Rose/ObjectPro is priced at $1,495. For more information contact
  18377. Trinzic Corporation, 555 Twin Dolphin Dr., Paragon Center, Redwood City, CA
  18378. 94085; (415) 328-9595.
  18379.  
  18380.  
  18381. AccuSoft Announces Pro-Imaging Toolkit
  18382.  
  18383.  
  18384. AccuSoft Corporation has announced the Pro-Imaging Toolkit, a DLL which
  18385. includes image processing and analysis support for 1-, 4-, 8-, and 24-bit
  18386. images. The Pro-Imaging Toolkit supplies routines for accessing and modifying
  18387. Windows DIB images. Routines in the Pro-Imaging Toolkit include chroma-key,
  18388. region-of-interest processing, image analysis, image processing, color
  18389. separation and combination, deskew, rotate and edge enhance, clipboard
  18390. support, and graphics. The Pro-Imaging Toolkit provides access to both high-
  18391. and low-level routines.
  18392. Other features of the Pro-Imaging Toolkit include samples with source code and
  18393. GUI support combined with a manual.
  18394. The Pro-Imaging Toolkit is priced at $795 for 16-bit libraries and $1,495 for
  18395. 32-bit NT. For more information contact AccuSoft Corporation, 2 Westborough
  18396. Business Park, Westborough, MA 01581; (508) 898-2770; FAX: (508) 898-9662.
  18397.  
  18398.  
  18399.  
  18400. IST and QNX Integrate X-Designer and QNX Realtime Operating System
  18401.  
  18402.  
  18403. Imperial Software Technology has announced the integration of its Graphical
  18404. User Interface builder, X-Designer, with the QNX Realtime Operating System
  18405. from QNX Software Systems. QNX is a POSIX-certified, network-distributed
  18406. operating system which is scalable from compact embedded systems to networks
  18407. running hundreds of processors. X-Designer can be used to generate user
  18408. interfaces in standard code for Motif, Windows, or Macintosh users.
  18409. X-Designer provides geometry management capabilities, internationalization,
  18410. HyperText Help, a compound string editor, and other features. X-Designer uses
  18411. no proprietary libraries or code and can be integrated with third party
  18412. products.
  18413. For more information contact Imperial Software Technology, 95 London St.,
  18414. Reading, Berkshire RG1 4QA, United Kingdom; +44 734-587055; FAX: +44
  18415. 734-589005; E-mail: sales@ist.co.uk; or V. I. Corporation, Northhampton, MA;
  18416. (413) 586-4144.
  18417.  
  18418.  
  18419. Aladdin Introduces HASPCard
  18420.  
  18421.  
  18422. Aladdin Software Security has introduced the HASPCard(tm). This PC expansion
  18423. card combines a standard HASP software protection key with a parallel port,
  18424. and provides enhanced security by having the key inside the PC. An internal
  18425. connection makes HASP viable when external access to the parallel port is
  18426. inconvenient or unavailable. Turnkey system vendors can now incorporate HASP
  18427. protection as an integral part of their system. Finally, the internal HASPCard
  18428. minimizes the risk that the card might be lost, damaged, or stolen.
  18429. Since HASPCard functions like regular HASP keys, current HASP-protected
  18430. applications will not need source code changes or recompilation to guarantee
  18431. compatability with the card. Three types of HASP keys are available in
  18432. HASPCard format: HASP-3, MemoHASP, and NetHASP.
  18433. HASPCard is available for an increment of $18 per unit over current HASP
  18434. implementations. For more information contact Aladdin Software Security Inc.,
  18435. The Empire State Bldg., 350 5th Ave., Suite 7204, New York, NY 10118; (800)
  18436. 223-4277 or (212) 564-5678; FAX: (212) 564-3377.
  18437.  
  18438.  
  18439. LEAD Ships LEADTOOLS Visual Basic/VBX 4.0
  18440.  
  18441.  
  18442. LEAD Technologies has begun shipping LEADTOOLS Visual Basic/VBX 4.0, a
  18443. royalty-free toolkit, which allows software developers to add image
  18444. compression and manipulation to any Visual Basic, Visual C++, or any other
  18445. application that provides a Visual Basic interface.
  18446. LEADTOOLS VBX handles checking file formats, reading files, compressing or
  18447. decompressing data, and displaying images. The VBX includes a property to link
  18448. to all databases supported by Visual Basic such as Access, dBase, Clipper, and
  18449. Fox Pro. The toolkit supports over 42 file formats including Kodak PhotoCD,
  18450. PCX, GIF, TGA, BMP, TIFF, JPEG, JFIF, JTIF, and LED CMP, LEADTOOLS can also be
  18451. used as a Windows DLL, a DOS executable, or a Visual C++ or Basic custom
  18452. control. LEADTOOLS supports both 16-and 32-bit Windows development.
  18453. For more information contact LEAD Technologies, Inc., 900 Baxter St.,
  18454. Charlotte, NC 2804; (704) 332-5532; FAX: (704) 372-8161.
  18455.  
  18456.  
  18457. General Imaging Announces S/IP 80
  18458.  
  18459.  
  18460. General Imaging has announced a single-slot, form-factor SBus card, providing
  18461. up to one billion operations per second. Follow-on versions targeted for first
  18462. quarter of 1995 and 1996 will run at 1.6 and 2.0 billion operations per
  18463. second, respectively. The S/IP 80, is an SBus C80 card for signal- and
  18464. image-processing applications, built around Texas Instrument's TMS 320C80, and
  18465. bundles General Imaging's signal/image processing framework, ProtoPIPE.
  18466. ProtoPIPE provides a graphical programming environment for object-oriented
  18467. graphic designs targeting the C80 chip for such applications as radar,
  18468. remote-sensing, GIS, medical, and machine vision.
  18469. The S/IP80 hardware includes a multi-standard video-digitizer, capable of
  18470. capturing live video and performing real-time processing. An additional audio
  18471. CODEC provides support for sound processing applications. The complete
  18472. hardware/software package is priced at $27,000.
  18473. For more information contact General Imaging Corporation, 6 Fortune Dr.,
  18474. Manning Park, Billerica, MA 01821; (800) 255-1577 or (508) 262-2262; FAX:
  18475. (508) 262-0088.
  18476.  
  18477.  
  18478. Cascadilla Press Lowers Price of Help Browser License
  18479.  
  18480.  
  18481. Cascadilla press has lowered the price of a developers license for Help
  18482. Browser 2.0. Help Browser is a utility which makes it easier to find
  18483. information in Windows 3.1 help files. Help Browser provides an interactive
  18484. map of the help file, allows for word and phrase searches, and supports
  18485. printing multiple topics.
  18486. With a developer's license for Help Browser, developers can add the features
  18487. of Help Browser to their Windows 3.1 application help files for distribution.
  18488. Control over the interactive map and text search tools can be customized. When
  18489. distributing Help Browser with an application, you include a special version
  18490. of the Help Browser along with a database file created by the full version of
  18491. Help Browser.
  18492. A developer's license for Help Bowser 2.0 is priced at $179 and includes the
  18493. right to distribute an unlimited number of copies of the embeddable version of
  18494. Help Browser. The end-user version of the Help Browser, which works with
  18495. Windows 3.1 help files, is priced at $89 and site licenses are available. For
  18496. more information contact Cascadilla Press, P.O. Box 440355, Somerville, MA
  18497. 02144; (617) 776-2370; FAX: (617) 776-2271; E-mail: info@cascadilla.com.
  18498.  
  18499.  
  18500. COSMIC Announces COSMIC C Cross Compilers
  18501.  
  18502.  
  18503. COSMIC Software, Inc. has announced the COSMIC C cross compilers for
  18504. Motorola's 68HC11 and 68HC16 microcontrollers. Highly optimized, ANSI C and
  18505. ISO standard compliant, COSMIC C cross compilers also include header file
  18506. support for on-chip peripherals. Other features of COSMIC C cross compilers
  18507. include compile-time type checking, C run-time support, extensions to the ANSI
  18508. standard designed for embedded systems, support for 68HC16 memory models, and
  18509. C source-level cross debugging.
  18510. The COSMIC C compiler package includes: an optimizing C cross compiler, macro
  18511. assembler, linker, librarian, object inspector, hex file generator, object
  18512. format converters, debugging support utilities, run-time library source code,
  18513. and a multi-pass compiler command driver. Prices for the COSMIC C cross
  18514. compilers start at $1,500. For more information contact COSMIC Software, Inc.,
  18515. 100 Tower Office Park, Woburn, MA 01801; (617) 932-2556; FAX: (617) 932-2557.
  18516.  
  18517.  
  18518. Lenel Systems Announces MediaRecorder Toolkit
  18519.  
  18520.  
  18521. Lenel Systems International has announced the MediaRecorder Toolkit. With
  18522. MediaRecorder Toolkit, Windows developers can integrate video capture
  18523. capabilities into their applications. With MediaRecorder Toolkit's OLE
  18524. automation support, OLE controls, C++ libraries, and DLLs, developers can
  18525. integrate MediaRecorder as a component into their multimedia applications.
  18526. MediaRecorder Toolkit includes a video capture component and an image capture
  18527. component. The video capture component provides methods for real-time digital
  18528. video captures, single video frame capture to a variety of graphics formats,
  18529. video thumbnails, and digital video compression. The image capture component
  18530. provides methods for capturing graphics files in formats such as BMP, JPEG,
  18531. TIF, GIF, PCX, TGA, and DIB. MediaRecorder Toolkit also includes palette
  18532. capture, still frame capture into any industry-standard file format, resizable
  18533. windows, and support for Windows-compatible video capture boards.
  18534. MediaRecorder Toolkit is priced at $595. There is no runtime license fee for
  18535. non-commercial products. For more information contact Lenel Systems
  18536. International, Inc., 290 Woodcliff Office Park, Fairport, NY 14450-4212; (716)
  18537. 248-9720; FAX: (716) 248-9185.
  18538.  
  18539.  
  18540.  
  18541.  
  18542.  
  18543.  
  18544.  
  18545.  
  18546.  
  18547.  
  18548.  
  18549.  
  18550.  
  18551.  
  18552.  
  18553.  
  18554.  
  18555.  
  18556.  
  18557.  
  18558.  
  18559.  
  18560.  
  18561.  
  18562.  
  18563.  
  18564.  
  18565.  
  18566.  
  18567.  
  18568.  
  18569.  
  18570.  
  18571.  
  18572.  
  18573.  
  18574.  
  18575.  
  18576.  
  18577.  
  18578.  
  18579.  
  18580.  
  18581.  
  18582.  
  18583.  
  18584.  
  18585.  
  18586.  
  18587.  
  18588.  
  18589.  
  18590.  
  18591.  
  18592.  
  18593.  
  18594.  
  18595.  
  18596.  
  18597.  
  18598.  
  18599.  
  18600.  
  18601.  
  18602.  
  18603.  
  18604.  
  18605.  
  18606.  
  18607.  
  18608.  
  18609. We Have Mail
  18610. Dear Editor,
  18611. Bjarne Stroustrup says of reference arguments (The C++ Programming Language,
  18612. 2nd Ed. p. 62), "However, to keep a program readable it is in most cases best
  18613. to avoid functions that modify their arguments." Dan Saks, however, says
  18614. (C/C++ Users Journal September 1994 p.89)
  18615. int genq::remove(void *&e) {
  18616. ...
  18617. e = p->element;
  18618. ... }
  18619. Richard Clarke says I don't much care who's right but agreement needs to be
  18620. made one way or the other.
  18621. Yours faithfully,
  18622. Richard A. Clarke
  18623. R.Clarke@ee.surrey.ac.uk
  18624. There are indeed two schools of thought on functions with side effects. A pure
  18625. functional programming style views side effects within a function call (or
  18626. indeed within any expression) as dangerous. The argument is that the code has
  18627. surprising effects not directly discernible from reading the expression that
  18628. calls the function. C/C++ programmers, in general, are more pragmatic. They
  18629. cheerfully write autoincrement operators, embedded assignments, and function
  18630. calls with side effects -- whatever gets the job done with an economy of
  18631. notation and executable code. Stroustrup seems to be arguing for a tempered
  18632. version of pure functional programming. Saks shows the valid use for reference
  18633. parameters (as opposed to const reference parameters). Programming practice is
  18634. too diverse for me to see a serious contradiction here.-- pjp
  18635. P. J. Plauger, Editor, C/C++ Users Journal:
  18636. Have you ever needed a rewind function for C++ istreams? The usual alternative
  18637. is the less elegant pair of calls:
  18638. istrm.seekg(0);
  18639. istrm.clear();
  18640. Instead, I use a rewind manipualor, which I define in the file rewind.h (see
  18641. Listing 1).
  18642. The rewind manipulator does for C++ istreams what C's rewind function does for
  18643. C file streams. Note that rewind can be used with any descendant of istream
  18644. but is most appropriate with ifstreams.It's used like this:
  18645. #include "rewind.h"
  18646. ...
  18647. ifstream InputStream("filename");
  18648. InputStream >> a >> b >> c;
  18649. // read three items from InputStream
  18650. InputStream >> rewind >> a2 >> b2 >> c2;
  18651. // rewind and read three more items
  18652. // note: a==a2 && b==b2 && c==c2
  18653. In the above example, a and a2 are extracted from the same part of InputStream
  18654. (as are b and b2, and c and c2).
  18655. Please share this code with your readers if you think they'll find it useful.
  18656. (The code also serves to demonstrate one way to define C++ manipulators and
  18657. how easy it can be.)
  18658. Paul Hepworth
  18659. s165r@cc.usu.edu
  18660. Yes, manipulators have endless possibilities. To comply with the draft C++
  18661. Standard, you have to modify this in several small ways, but the idea is
  18662. basically a good one. -- pjp
  18663. Editor,
  18664. Thank you for a wonderful magazine! I have been a reader for many years and a
  18665. subscriber for several. I get several C-oriented publications off the shelf on
  18666. an irregular basis, but I have found that only your magazine has enough
  18667. information to the general programmer to subscribe to.
  18668. Thank you also for the fax-back service offered from the magazine. Many of
  18669. these are available for various publications, but they are all long-distance.
  18670. I hope that you can continue offering this service to your readers in the
  18671. future. (I understand that someone must foot the bill, and if it's not me,
  18672. it's probably your publication.) If you could pass on my thanks to those
  18673. businesses who support this service I would appreciate it! At my day job, my
  18674. office can afford the long distance calls, but for my after-hours programming
  18675. and personal work, I cannot. Some of these advertisers may recognize my name
  18676. or my company's name as information obtained through this service has led to
  18677. several purchases for myself, and for my office.
  18678. This service is wonderful for the struggling small business that watches every
  18679. penny. I have recently spent a fair amount of money to get my home business
  18680. started on the right foot and I would not have been able to get as much as I
  18681. did, if it weren't for this service!
  18682. Once again, I thank you for providing this service, and I thank your
  18683. advertisers for participating!
  18684. Jeffry Brickley
  18685. Lockheed Engineering and Sciences (by day)
  18686. TimeWarp Software (by evening)
  18687. Dear P.J.Plauger,
  18688. I am writing a C++ program for Windows using Borland C++ 4.0. The program
  18689. makes use of a large tree of small objects, which is built during
  18690. initialization. After initialization the tree is read-only during the rest of
  18691. its lifetime. The program worked fine until the size of the tree approached 64
  18692. KB; seemingly, I ran out of memory. Under Windows memory is split into "near
  18693. heap" (near pointers, total size < 64 KB) and "far heap" (far pointers, total
  18694. size: all free memory). The problem arose because I used the C++ operator new
  18695. to allocate objects. new works only on the near heap. It returns a near
  18696. pointer (void near*), and you cannot overload new to return a far pointer
  18697. (void far*). I am not sure how general the problem is, but my solution may be
  18698. of general interest.
  18699. To solve the problem objects must be allocated on the global heap, of course,
  18700. paying the penalty of accessing the objects via far rather than near pointers.
  18701. I must find ways: (i) of managing the global heap and (ii) of allocating
  18702. objects there. With regards to (i), a very simple implementation is possible,
  18703. because all objects are created at initialization time and remain unmodified
  18704. until the end of execution. (See Listing 2.)
  18705. Function New returns a far pointer to a memory block of the requested size.
  18706. You cannot specifically delete such a block, rather the whole Heap object goes
  18707. up in flames as its destructor is called. GlobalAllocPtr and GlobalFreePtr are
  18708. Windows functions for maintenance of the global heap. To find the address at
  18709. the offset pos bytes within the current block, the starting address is cast to
  18710. a pointer to character. The pos added will then count as bytes.
  18711. To address (ii), I have this suggestion for allocating objects of some class X
  18712. on the global heap:
  18713. extern Heap heap;
  18714. class X {
  18715. ...
  18716. virtual X far* Clone()
  18717. { X far* p =heap.New(sizeof(X));
  18718. *p = *this;
  18719. return p;
  18720. }
  18721. };
  18722. The Clone function simply makes a copy of the X object on the global heap and
  18723. then returns a pointer to that copy. The copy constructor of X should be
  18724. implemented to ensure that the assignment above works out. If deriving a class
  18725. Y from X, it should have an implementation of Clone() similar to this.
  18726. Otherwise, X::Clone() would be called for Y objects with disaster soon to
  18727. follow.
  18728. The above definitions could be used like this:
  18729. // Global heap object
  18730. Heap heap;
  18731.  
  18732.  
  18733. main(){
  18734. // Three far pointers to X;
  18735. // initialized in different ways (i-iii):
  18736. X far* pa, far* pb, far* pc;
  18737.  
  18738. // (i) From a local object
  18739. X a("Rip", 2);
  18740. pa = a.Clone();
  18741.  
  18742. // (ii) From an object on near heap
  18743. X near* b;
  18744. b = new X("Rap", 3);
  18745. pb = b->Clone();
  18746. delete b;
  18747.  
  18748. // (iii) From a temporary object
  18749. pc = X("Rup", 7).Clone();
  18750. }
  18751. Any comments on my design in general or on heap management under Windows in
  18752. particular are welcome.
  18753. Yours,
  18754. Niels Holst,
  18755. Zoological Institute,
  18756. University of Copenhagen,
  18757. Denmark
  18758. E-mail: NHo1st@zi.ku.dk
  18759. Unless I'm missing something, it seems to me that placement new is a better
  18760. mechanism for this sort of thing. -- pjp
  18761. Dear Editor:
  18762. I have an application for which I would like to generate random English
  18763. pronounceable proper names. I have looked in the common sources (Knuth,
  18764. Sedgewick, Wirth, etc.) for a suitable algorithm, but haven't had much luck.
  18765. Have you ever come across such an algorithm or one that could be adopted?
  18766. Sincerely,
  18767. Edward B. Bell
  18768. Integrated Solutions, Inc.
  18769. 6618-18 Reafield Drive
  18770. Charlotte, NC 28226
  18771. No, but I can think of some puckish applications for such an algorithm. I hope
  18772. one of our readers can help you find one. -- pjp
  18773. Dear CUJ,
  18774. There is a bug in Listing 1 on page 78 of your April 1994 volume. The loop
  18775. control variable i is incremented within the initialization loop with the
  18776. result that only even indices of the freq_count array are set to zero. Also,
  18777. errors will occur if characters are signed and strings containing characters
  18778. with ordinal values 80-FF hex are input, since this will yield negative
  18779. indices into the freq_count array.
  18780. Sincerely,
  18781. John S. Hanus
  18782. Houston TX, 770
  18783. P.S. Would you please tell me how I might obtain a recent salary survey for
  18784. computer professionals?
  18785. Thanks for the bug report. You might try one or more of the IEEE publications.
  18786. Seems to me they publish regular surveys covering our field. -- pjp
  18787. Dear Mr. Plauger,
  18788. When I saw an article about an extended date library in your October, 1994
  18789. issue, I was hopeful. There is just so much disinformation these days about
  18790. calendars and dates that I had hoped this article would clear up some of the
  18791. confusion. Unfortunately, I was somewhat disappointed after reading the
  18792. article.
  18793. In the article, Mr. Milam explains that his date function "returns the number
  18794. of days elapsed since 1 January, 0001 A.D." Such a statement implies that the
  18795. date function is valid all the way back to that date. I often find myself
  18796. dealing with dates in the Middle Ages, so this looked like something I could
  18797. use.
  18798. Being skeptical, however, I decided to check the accuracy of that claim,
  18799. specifically for dates before October 15, 1582, the date the Gregorian
  18800. calendar went into effect. (For practical purposes, this is true even though
  18801. some countries did not accept the Gregorian calendar until several centuries
  18802. later. Regardless of when the calendar was accepted, there is a discontinuity
  18803. caused by the removal of ten days so that Easter would fall closer to the
  18804. spring equinox. In the Catholic Church, this discontinuity occurred between
  18805. October 4th and October 15th, 1582.)
  18806. To his credit, Mr. Milam does mention in a sidebar that "This value presumes
  18807. the Gregorian calendar in use today was implemented on 1 January, 0001 A.D.,
  18808. (which of course, it was not)..." No mention of this is made in the main
  18809. article, however, which might cause some people to be misled into thinking
  18810. that the date function can be used for calculations, such as day of the week,
  18811. and days between dates, regardless of what side of October 15, 1582 the days
  18812. fall. In fact, the assertion that the value of date is the number of days
  18813. since January 1, 0001 A.D. is false even for days after October 15, 1582
  18814. because before this date, the Gregorian calendar and the Julian calendar
  18815. should be identical.
  18816. As an example, date will return a value of 728,202 for October 1, 1994, but
  18817. the correct value is 728,203. The difference is due to errors in two places.
  18818. The first, as noted before, is due to the discontinuity in the Gregorian
  18819. calendar and the different method of calculating leap years before that date.
  18820. In order to compensate for this, the following adjustments must be made for
  18821. dates occurring after October 4, 1582:
  18822. Adjustment for leap centuries divisible by 400: --(1582 / 400) --3
  18823. Adjustment for leap centuries divisible by 100: +(1582 / 100) +15
  18824. Adjustment for dates removed from calendar: --10
  18825. Cumulative adjustment: +2
  18826. The other error is in not adjusting for January 1, 0001 since, by definition,
  18827. zero days would have elapsed between this date and itself. I therefore
  18828. subtracted one to obtain the correct result. In order to obtain the correct
  18829. result for dates after October 4, 1582, the result should therefore be
  18830. increased by one. For dates before this, the year calculation should omit the
  18831. adjustments for leap centuries and then one should be subtracted from the
  18832. result to account for January 1, 0001.
  18833. The modified code would appear as shown in Listing 3.
  18834. While sidebar articles are a nice way to delve into related subjects, I
  18835. sometimes don't read them unless I'm interested in more background
  18836. information. In my opinion, information which relates to the correct use of a
  18837. program or procedure library should appear in the main text of the article.
  18838. Even with all of my complaints, I am glad to see someone publishing something
  18839. about dates and calendars. This is practical information we all can use. I
  18840. look forward to seeing more articles of this kind in the future.
  18841. Sincerely,
  18842. Michael R. Kabala
  18843.  
  18844. Davenport, IA 52807
  18845. Every time I think I have nothing new to learn about calendars... Thanks. --
  18846. pjp
  18847. Greetings:
  18848. What is this that I see in the July 1994 issue of CUJ? Why it's a YASM (yet
  18849. another style manual). Complete with the imperative shall. I guess we are
  18850. doomed to repeat history endlessly.
  18851. Some 15 years ago I began writing code with curly braces in two languages --
  18852. Ratfor and Pascal. The curly braces were used for entirely different reasons
  18853. in the languages and it created no small amount of confusion amongst my Pascal
  18854. colleagues when I would show them my Ratfor code. And, naturally, curly braces
  18855. were not treated at all kindly by the Fortran crowd.
  18856. So, I took a defensive tack and put some macros in the wrapper for my Ratfor
  18857. source code that defined begin as { and a variety of end statements (end_for,
  18858. end_do, end_if, end_while, etc.) as }. While I was at it, I equated the ==, !=
  18859. &&, // notations to Fortran like symbols eq, ne, and, or respectively, and !
  18860. to NOT. Moreover, the if-then-else craze was in so I defined and used an empty
  18861. then statement at the end of my if statements. My Fortran friends settled down
  18862. some; my Pascal pals seemed mollified.
  18863. When I migrated to C in 1982 I took most of this baggage with me. Using this
  18864. stuff, I talked several assembler fanatics into trying C. What really turned
  18865. them on were the ++ and -- operators. One of the benefits of using
  18866. lettered-operators was that my supervisors, who typically knew little or no C,
  18867. could read my code. I wrote thousands of lines of C code this way.
  18868. Then, in 1986 I moved to an organization where C was the preferred language.
  18869. The programmers were, by and large, C strict constructionists. To them, what I
  18870. had written simply wasn't C at all. Well, a hybrid maverick dialect maybe --
  18871. but unacceptable. So I ended up writing a tool in lex to convert my lovely
  18872. code (KB's stuff, they called it) to regular C. I converted thousands of lines
  18873. of my library utilities.
  18874. The point in all this palaver is that I have come to the conclusion that it is
  18875. preferable to stick to the language as provided and let the uninitiated
  18876. readers and bystanders fend for themselves. As for team members, if they are
  18877. carrying excess baggage from another language, they can hoist it overboard in
  18878. time. Moreover, if new members to the team are experienced C programmers,
  18879. their learning time is shortened.
  18880. Successful software projects are the result of sound programming principles.
  18881. Careful, thoughtful, thorough design, coding and checkout will do more for a
  18882. programming project that all of the synthetic paste-ons that the new idealists
  18883. love to promulgate.
  18884. Sincerely,
  18885. K. B. Williams
  18886. Stillwater, OK
  18887. I agree with most of what you say, with an added proviso. Whatever a
  18888. programming group settles on in the way of a uniform style is always better
  18889. for the group effort than any individual's notion of good style. , -- pjp
  18890. Dear Mr Plauger,
  18891. Apparently I'm one of the remaining few who find C's attractions outweigh its
  18892. many distractions. Even at this late date, I find myself keeping an eye out
  18893. for books and articles on C in the hope that yet another of its hidden truths
  18894. may surface, intentionally or unwittingly revealed at last. I do this because
  18895. I see in C a language whose real potential lies rooted in its remarkable if
  18896. somewhat inchoate, capacity to support novel, even advanced programming
  18897. techniques and dialects.
  18898. Unfortunately this aspect of C is rarely touched on publicly. C's popularizers
  18899. tend to spend their time re-hashing its syntax and precedence rules or they
  18900. make a career out of reminding us of the tricks and traps any respectable
  18901. construct is capable of entertaining. Occasionally they even go on and on
  18902. about the intricacies of the command-line, mentioning its limited relevance
  18903. for today's world.
  18904. Project-sized program development is where it's at today, moving the
  18905. programmer's concerns so far beyond the command line that it's almost become a
  18906. fossil. For one thing, we no longer test from the command line with debug
  18907. options unless we're nearly brain-dead in the first place.
  18908. So what then if while testing your latest fantabulous brain-child, your
  18909. computer goes idling off into infinity, hobnobbing with chaos, dancing the
  18910. herky-jerky (formerly known as the black death), what else is new? If you know
  18911. what you're doing within a matter of minutes you've located the trap-door
  18912. through which that errant blankety-blank fell.
  18913. You're able to do that because the limbo of faulty technique is, itself,
  18914. flawed. It can't even rear its ugly head for a last malicious leer without
  18915. inadvertently revealing its odiferous self. That's because design problems
  18916. (apparently with teleological foresight) have always excelled at setting what
  18917. appear to be bottomless-pit booby-traps, even though they actually aren't.
  18918. The rumor persists that they exceed code problems as breeders of man-eating
  18919. bugs, so we must at least continue pretending to respect them. Just be
  18920. careful, though, where you seek help when you think what you've got is a code
  18921. problem. Unless you're working on someone else's grossly over-extended
  18922. project, are trying to mesh with the other guy's buggy code, or are voyaging
  18923. into a complex, new (is this trip really necessary) environment, you'll find
  18924. debuggers largely irrelevant. If you can learn to avoid the swamps of fantasy
  18925. land, you'll rarely need one.
  18926. That doesn't mean we've all suddenly become omniscient. Most programmers (if
  18927. they're any good at all) do serve a life-long apprenticeship. At least, they
  18928. do as far as the analysis and design stages of program development are
  18929. concerned; but if they wallow in the myriad ways to code this or that
  18930. construct, they're rarely very productive. And yes, there are always beginners
  18931. thirsting for knowledge of the basics, but the day they monopolize anyone's
  18932. readership to the exclusion of all else, someone's in more trouble than they
  18933. realize.
  18934. That may be why it's a good idea for any programmers' periodical to include
  18935. coverage of the big picture; if not regularly at least frequently. I don't
  18936. think, for example, that I'd need the fingers on more than one hand (not even
  18937. another digit or two) to count the number of times I've come across a
  18938. treatment anywhere of the semantics of programming in general, let alone that
  18939. of C.
  18940. Many of us, believe it or not, have only an occasional or quickly passing
  18941. interest in bit manipulation, pointer arithmetic, multi-level indirection,
  18942. type conversion tricks, porting, porting, porting, etc, etc, etc. We deal with
  18943. these matters rarely but carefully; once we've found reliable,
  18944. results-oriented methods for handling them, that's it. We definitely ain't
  18945. preoccupied with or troubled by them no more.
  18946. And certainly, none of us has to write many programs in any language before we
  18947. realize we've bigger fish to fry if we're to become proficient. Even beginners
  18948. soon grasp the significance of learning the system requirements for putting
  18949. together multi-module projects. (Another subject I've never seen treated
  18950. adequately anywhere.)
  18951. Of course, acquiring the skills needed for tackling larger projects does
  18952. entail, among other things, learning to handle memory management, which in
  18953. turn requires one to become agile at sidestepping any guru gobbledegook espied
  18954. along the way, especially the kind designed to persuade the gullible they're
  18955. facing another of those ubiquitous, "mystery wrapped in an enigma" boogeymen
  18956. the Elect erect around every turn in the road.
  18957. It also means learning to juggle a motley crew of I/O knives, plates, and
  18958. bowling balls with considerable skill and dexterity (now there's some cans
  18959. waiting to be opened).
  18960. It doesn't hurt a bit either if, somewhere along the way, one works up a
  18961. minimally comprehensive, rock-solid library of heap-oriented Abstract Data
  18962. Types (perhaps some of those very ADTs so summarily dismissed from public
  18963. scrutiny when OOPing first appeared on the scene). Without them, even in C
  18964. only a limited level of sophistication is possible.
  18965. Of course, an alternative to using your own ADT library is to use C++. But
  18966. replacing your ADTs with C++'s Ptolemaic version of the OOP-iverse can do
  18967. strange things to your view of the night sky. True, C++ does have its merits.
  18968. It also has its share of leaky balloons, sluggish ships of the OOP-osphere,
  18969. unable to stay aloft without a lot of huffing and puffing by the latest
  18970. version of the cutting-edge.
  18971. Suppose we did elect to use C++ selectively and rarely, preferring instead to
  18972. develop our own in-house C dialects, would that mean the language might soon
  18973. find itself teetering on the edge of the Babelization precipice? Not to worry.
  18974. C's been perched precariously on the next ledge over for years. Who among us
  18975. hasn't already found it annoying (or worse) to have to read radically
  18976. different styles of perfectly legitimate C code on a regular (almost daily)
  18977. basis?
  18978. (Let's let he who cast the first stone... My guess is he's probably gonna do
  18979. it anyway.)
  18980. Quickly now, do you know of any other programing language in the whole
  18981. computerized world that lets you almost completely redo itself for your own
  18982. nefarious purposes? Where else can you erect a highly articulate, linguistic
  18983. super-structure of your own design without risking the wrath of The Great Pooh
  18984. Bah? Isn't this what programming at its most fascinating and fullfilling is
  18985. all about? After one strips away the vestiges of commercialism, with its
  18986. transient aura of achievement and reward (even earthly power for those on an
  18987. egotrip), what's left?
  18988. Haven't we always entertained (secretly of course) a yen to use our computers
  18989. as extensions of our minds? Well, C is one programming language (par exellence
  18990. at that) which allows us to do just that. It beckons us to explore and invent.
  18991. It invites us to create new, viable alternatives (ultimately perhaps, even one
  18992. to supplant itself).
  18993. When you get right down to it, I'm sure this has a lot to do with why I still
  18994. count myself among the remaining (lucky) few.
  18995. Truly,
  18996. Mark Rhyner
  18997. Uh, yeah. Thanks for sharing those thoughts. -- pjp
  18998. Gentlemen:
  18999. I have discovered a bug in the code for your program dump.c on page 70 of the
  19000. September 1994 issue of The C/C++ Users Journal. The following line of code:
  19001. fprintf(stderr, "Can't open %s\n");
  19002. should read as follows:
  19003. fprintf(stderr, "Can't open %s\n",
  19004. argv[1]);
  19005. Otherwise the file name is displayed as machine garbage.
  19006. I have been a faithful reader/subscriber to The C/C++ Users Journal for over
  19007. five years now and a C user for over seven years, although at this point I
  19008. still just dabble in the "basement." I have found your magazine to be quite
  19009. useful and have saved every issue since I became a subscriber. I felt I should
  19010. bring the aforementioned program bug to your attention.
  19011. Sincerely,
  19012. David P. Williams
  19013. Thanks. -- pjp
  19014. Dear Mr. Plauger:
  19015. With interest I read the letters of Mr. Singleton and Mr. Larsson concerning
  19016. the memory allocation within Windows 3.[01] in January issue of C/C++ Users
  19017. Journal.
  19018. Isn't it interesting how something like malloc can be screwed up in this
  19019. wonderful OS? I usually program in the dated and complicated OS UNIX where I
  19020. simply call malloc and don't have to worry about segmented architectures. I
  19021. wonder why people think that Windows is such a great improvement over other
  19022. operating systems. Could it be that they really mean is it is an improvement
  19023. over DOS?
  19024. Agreed, it is nice to have all of these colorful icons and windows. Agreed, it
  19025. is easy to use as long as it does not hang up, destroy five hours of work or
  19026. anything like that. However it is a pain in the neck when it comes to
  19027. programming it.
  19028. I once had to port a UNIX library to Windows and believe me, I think it is a
  19029. very good reason to get out of the programming business altogether and start
  19030. selling newspapers or do anything that makes sense.
  19031. Sincerly,
  19032. Detlef Engelbrecht
  19033. Hamburg, Germany
  19034. detlef@isys.net
  19035. A segmented architecture certainly adds complexities, particularly for a
  19036. language like C that evolved on machines with flat address spaces. We can hope
  19037. that the emerging 32-bit flavors will eventually end the need for several
  19038. flavors of pointers, and storage allocators to go with them. -- pjp
  19039. Editor:
  19040.  
  19041. I hereby add my voice to those who enjoy CUJ, and wish we had found it sooner.
  19042. (And, being slow to convert, would like the scales tipped in the direction of
  19043. C)
  19044. The "Conversions and Casts" article by Chuck Allison in the September 1994
  19045. issue of CUJ was excellent. Articles of this type provide review and
  19046. instruction to us all.
  19047. I would question one section of the article (and I may be misreading what he
  19048. is saying). In the Arithmetic Conversions section, Mr. Allison states that
  19049. "The roundoff error inherent in finite-precision arithmetic caused the product
  19050. 100 * 23.4 to land closer to 2339 than 2340." C does not round when it
  19051. converts from a float to an int, it truncates.
  19052. Because, as Mr. Allison points out, floating point numbers are only
  19053. approximations and not exact (the degree of approximation being
  19054. machine-dependent), the result of a floating-point multiplication of 100
  19055. (which is implicitly promoted to a float) and 23.4 is represented in the
  19056. machine as a fraction less than 2340.0. Therefore, when the result is assigned
  19057. to an int, the precision is truncated, and only the integer portion is
  19058. assigned. The reason the conditional statement inside the printf also fails to
  19059. recognize the result to be 2340.0 is again because of the result being an
  19060. approximation which is compared against a constant exact value of 2340.0.
  19061. My apologies if I have misread the text of the article, but I thought it
  19062. needed to be made clear.
  19063. George B. Durham
  19064. Your explanation is accurate enough. I believe, however, that Chuck Allison
  19065. was using "roundoff" in the most general sense. "Rounding to lower" is a fancy
  19066. term for truncating, and "rounding to nearest" is another term for the
  19067. colloquial rounding. -- pjp
  19068. Dr. P.J. Plauger:
  19069. I just read your January review of Anthony Porter's book, The Best C/C++ Tips
  19070. Ever and I can't tell you how impressed I was at the way you shredded this
  19071. Porter character. I'll bet he was really surprised.
  19072. I would have been. For months your magazine has carried full-page ads for a
  19073. software giant who sells a buggy linker, a buggy debugger, and now it's
  19074. revealed (thanks to some of your competitors) even a buggy compiler.
  19075. Meanwhile, the vendor's customers are advised to call the company's $2.00 per
  19076. minute advisor line if they have complaints. What really surprises me is that
  19077. your apparently overwhelmingly strong sense of ethics hasn't compelled you to
  19078. mention something about maybe as many as one of these problems.
  19079. Mr. Porter's book was published by McGraw-Hill, while I notice that my copy of
  19080. your book The Standard C Library was published by Prentice-Hall. That probably
  19081. has no relevance to anything, but I'm trying to understand why you choose to
  19082. kill flies with a hatchet while you spray perfume on nuclear hooligans.
  19083. Perhaps you'd like someone to believe that because of your hatchet work you
  19084. really must be some sort high principled, rock-ribbed he-man editor. To me, it
  19085. indicates both a lack of principles and testicles.
  19086. Sincerely,
  19087. Bob Moore
  19088. cc: McGraw-Hill Publications
  19089. Your thorny prose demands a few well chosen responses. First, please note that
  19090. I did not shred "this Porter character." I shredded his book, which stands or
  19091. falls on its own merits. Ad hominem attacks say more about the attacker than
  19092. the target, as a rule. So too does a person's ability to distinguish between
  19093. criticism of things and people.
  19094. Second, I have personally written a number of books and several commercial
  19095. compilers, so I'm keenly aware how hard it is to do both. I am still actively
  19096. writing books and software, so I have scant time for much anything else. If I
  19097. review a book instead of a major software package, that speaks more to the
  19098. relative effort I can spare than to the relative importance a third party
  19099. might ascribe to the two efforts.
  19100. Finally, I have to confess that I care not one whit what you think about me or
  19101. my motives. Anthony Porter tried to write a good book and failed, at least in
  19102. my estimation. I respect that effort far more than I respect bystanders who
  19103. sling mud with neither restraint, accuracy, nor a sense of direction. -- pjp
  19104. Dear Mr. Plauger:
  19105. Since there has been so much interest in date routines lately, I could not
  19106. resist putting in my two cents worth.
  19107. The routine presented by Mr. Magalhaes in your January 1994 issue is, in fact,
  19108. a rendition of the formula presented by the great mathematician C.F. Gauss (1,
  19109. 2) which has the acknowledged limited domain of years. A more robust method
  19110. for calculating the date of (western) Easter which is valid for any date in
  19111. the Gregorian calendar, that is from the year 1583 on, is given in Listing 4.
  19112. I came across this method in a book by Meeus (3) in which he states that this
  19113. method was originally given by Spencer Jones in his book General Astronomy
  19114. (1922). It was then published again in The Journal of the British Astronomical
  19115. Association, vol. 88, page 91 (December 1977) where it is said that this
  19116. method was devised in 1876 and appeared in Butcher's Ecclesiastical Calendar!
  19117. The routine in Listing 4 can be made more efficient by using the divide with
  19118. remainder function instead of the repeated divisions. I merely present it in
  19119. this format as it may be easier for some to follow. For Those who are
  19120. interested, Listing 5 shows a method for calculating Julian Easter. It is
  19121. interesting to note that the date of Julian Easter has a periodicity of 532
  19122. years.
  19123. Well, my two cents are now up. Thanks for letting me babble and keep up the
  19124. good work. I enjoy the magazine very much.
  19125. References: (3) and (4) are general references for Gauss (and others)
  19126. [1] Gauss, C. F., Theory of the Motion of Heavenly Bodies (Dover Reprint,
  19127. 1963)
  19128. [2] Reicharot, Hans, ed., C. F. Gauss, Leben und Werk (Gauss Gedenkband,
  19129. Berlin: Haude & Spener, 1960)
  19130. [3] Meeus, John, Astronomical Formulae for Calculators (Willmann-Bell, Inc.,
  19131. 1988)
  19132. [4] Boyer, Carl B., A History of Mathematics (John Wiley & Sons, 1968)
  19133. [5] Bell, E. T., Men of Mathematics (Simon and Schuster, 1937)
  19134. Aris Karimalis
  19135. The two functions certainly have an elegant simplicity. Thanks. -- pjp
  19136. Editor,
  19137. (Pardon the informality, but) would you mind solving one simple office puzzle
  19138. for us in a future CUJ article/box? We regularly exit Windows to log users out
  19139. or in (allowed only in DOS, not a shell) to our Novell-based LAN at work.
  19140. Isn't there a procedure we could compile and place in batch to simplify this
  19141. clunky process?
  19142. Thanks,
  19143. Skip Pletcher
  19144. MenSana
  19145. I don't know squat about programming for Netware, but I know what you want
  19146. must be possible. My Netware Lite manager will execute a DOS batch script from
  19147. within Windows that can, among other things, log me onto the network. Anybody?
  19148. -- pjp
  19149.  
  19150. Listing 1 A rewind manipulator for istream
  19151. /* rewind.h vers 0.9
  19152. defines istream manipulator rewind
  19153. that "rewinds" input stream to
  19154. beginning of file and clear state
  19155. (to reset eof flag)
  19156.  
  19157. copyright 1994 Paul Hepworth
  19158.  
  19159. Permission is granted for everyone
  19160. to include this code in his/her
  19161. programs without restriction.
  19162. */
  19163. #if !defined_REWIND_H
  19164. #define_REWIND_H
  19165.  
  19166. #if !defined _IOSTREAM_H
  19167. #include <iostream.h>
  19168. #endif
  19169.  
  19170.  
  19171. class rewind_type
  19172. {
  19173. friend istream& operator>>(istream& is,
  19174. const rewind_type);
  19175. };
  19176.  
  19177. const static rewind_type rewind;
  19178.  
  19179. inline istream& operator>>(istream& is,
  19180. const rewind_type)
  19181. {
  19182. is.seekg(0);
  19183. is.clear();
  19184. return is;
  19185. }
  19186. #endif
  19187.  
  19188.  
  19189. Listing 2 Allocating global memory from windows
  19190. const unsigned BLOCKSIZE = 60*1024;
  19191. const int MAXBLOCK = 64;
  19192.  
  19193. class Heap{
  19194. void far* block[MAXBLOCK] // Array of far
  19195. // pointers to
  19196. // memory blocks
  19197. int n; // No. of blocks
  19198. // in use
  19199. unsigned pos; // Next vacant
  19200. // position within
  19201. // current block
  19202.  
  19203. public:
  19204. Heap()
  19205. { n = 0;
  19206. }
  19207. ~Heap()
  19208. { for (int i = 0; i<n; i++)
  19209. GlobalFreePtr(block[i]);
  19210. }
  19211. void far* New(unsigned nBytes)
  19212. {
  19213. // Allocate another block if necessary
  19214. if (n == 0 nBytes > BLOCKSIZE - pos){
  19215. assert(n < MAXBLOCK && nBytes <= BLOCKSIZE);
  19216. block[n++] = GlobalAllocPtr(GHND, BLOCKSIZE);
  19217. assert(block[n-1] != 0);
  19218. pos = 0;
  19219. }
  19220. // Get a pointer from within the current block
  19221. void far* p =
  19222. ((char far*) block[n - 1]) + pos;
  19223. pos += nBytes;
  19224. return p;
  19225. }
  19226. };
  19227.  
  19228. Listing 3 Code to correct Milam's Date Routines
  19229. static int is_it_a_ieap_year( unsigned year ){
  19230.  
  19231.  
  19232. if (year > 1582)
  19233. return ((year % 4 == 0 && year % 100 != 0)
  19234.  year % 400 = 0);
  19235. else return year % 4 = 0;
  19236. }
  19237.  
  19238. static date_t years to_days( unsigned year) {
  19239.  
  19240. date_t rv;
  19241.  
  19242. if (year > 0) year--,
  19243. rv = year * 365L + year / 4L;
  19244. if(year>= 1582)rv+=year/400L-year/ 100L+ 12;
  19245. return rv;
  19246. }
  19247. date_t time_to_date( time_t tv )
  19248.  
  19249. date_t rv;
  19250. struct tm *tm;
  19251. int year, leap_year;
  19252.  
  19253.  
  19254. /**********************************************/
  19255. /* Get a time structure to use for conversion
  19256. process. */
  19257.  
  19258. /*********************************************/
  19259.  
  19260. tm = localtime(&tv);
  19261.  
  19262.  
  19263. /*********************************************/
  19264. /* Use values in the tm structure to convert
  19265. the current */
  19266. /* date into a long integer value. */
  19267.  
  19268. /*********************************************/
  19269.  
  19270. year = tm -> tm_year + 1900;
  19271. leap_year = is_it_a_leap_year(year);
  19272. rv = years_to_days( year );
  19273. rv += months_to_days(tm -> tm_mon + 1,
  19274. leap_year );
  19275. rv += tm->tm_mday;
  19276. if((year> 1582) ((year== 1582)&&
  19277. ((tm->tm_mon > 10) 11 ((tm->tm_mon = 10)
  19278. && (tm->tm_mday > 14))
  19279. rv-= 10;
  19280. return rv - 1;
  19281. }
  19282.  
  19283.  
  19284. Listing 4 Calculating Gregorian Easter
  19285. /* Gregorian Easter */
  19286.  
  19287. int geaster (int year)
  19288. {
  19289. int A, B, C, D, E, F, G, H, I, K, L, M, Month, Day;
  19290.  
  19291.  
  19292. if (year < 1583) return 0;
  19293. A = year % 19;
  19294. B = year / 100;
  19295. C = year % 100;
  19296. D = B / 4;
  19297. E = B % 4;
  19298. F = (B + 8) / 25
  19299. G = (B - F + 1) / 3;
  19300. H = (19 * A + B - D - G + 15) % 30;
  19301. I = C / 4;
  19302. K = C % 4;
  19303. L = (32 + 2 * E + 2 * I - H - K) % 7;
  19304. M = (A + 11 * H + 22 * L) / 451;
  19305. Month = (H + L - 7 * M + 114) / 31;
  19306. Day + (((H + L - 7 * M + 114) % 31) + 1);
  19307. return (Month + Day * 10);
  19308. }
  19309. /* unwrap using Month = result % 10 */
  19310. /* Day = result / 10 */
  19311.  
  19312.  
  19313. Listing 5 Calculating Julian Easter
  19314. /* Julian Easter */
  19315.  
  19316. int jeaster (int year)
  19317. {
  19318.  
  19319. int A, B, C, D, E, Month, Day;
  19320.  
  19321. A = year % 4;
  19322. B = year % 7;
  19323. C = year % 19;
  19324. D = (19 * C + 15) % 30;
  19325. E = (2 * A + 4 * B - D + 34) % 7;
  19326. Month = (D + E + 114) / 31;
  19327. Day = ((D + E + 114) % 31) + 1;
  19328. return (Month + Day * 10);
  19329. }
  19330. /* unwrap using month = return % 10 */
  19331. /* day = return / 10 */
  19332.  
  19333.  
  19334.  
  19335.  
  19336.  
  19337.  
  19338.  
  19339.  
  19340.  
  19341.  
  19342.  
  19343.  
  19344.  
  19345.  
  19346.  
  19347.  
  19348.  
  19349.  
  19350.  
  19351.  
  19352.  
  19353.  
  19354. An Error Manager With Message Text Replacement
  19355.  
  19356.  
  19357. David Chapman
  19358.  
  19359.  
  19360. David Chapman is a Software Consultant specializing in VLSI CAD tools,
  19361. compilers, and language design. He has a BSCS from California Polytechnic
  19362. State University, San Luis Obispo, and an MSEE from Stanford University. He is
  19363. currently working on a research project in VLSI layout synthesis. He may be
  19364. reached at dchapman@netcom.com.
  19365.  
  19366.  
  19367.  
  19368.  
  19369. Introduction
  19370.  
  19371.  
  19372. Library developers often face a difficult problem: the need to report errors
  19373. without knowing what kind of environments the library routines will be used
  19374. in. For example, VLSI ComputerAided Design (CAD) software often runs under a
  19375. graphical user interface (GUI), based on the X-Window system. My code usually
  19376. does straight number crunching with little user interaction. However, VLSI CAD
  19377. software sells worldwide, wherever there is a local semiconductor industry. I
  19378. want to be able to translate my software to the language used by my customers
  19379. even though I don't speak any languages other than English (so far). Thus my
  19380. code needs a simple way to translate the error messages it prints.
  19381. The more commonly-used techniques for making programs configurable don't
  19382. support this need very well. Macintosh and Windows programmers are familiar
  19383. with resource files, which contain the printable text and some of the graphics
  19384. used by a program. Resource editors allow users to change the text associated
  19385. with error messages and prompts. Resource files exemplify a "heavyweight"
  19386. system, requiring a fair amount of source code and auxiliary files. Such a
  19387. system requires editing and checking in of both the source code and the
  19388. message file. On a project with only one message file being maintained by
  19389. multiple programmers, updates may be lost unless extreme care is taken.
  19390. I've maintained code based on other message systems that export the text, but
  19391. I've found them difficult to manage. Here's a sample message from one of those
  19392. systems:
  19393. raiseWarning(runOutShape,baseName,cellName);
  19394. Since the original author didn't comment this particular message, I had to go
  19395. look up the text so I could understand the code that used it.
  19396. Finally, in a CAD environment, the same code may run within a GUI or as a
  19397. non-graphical program, selected at run-time, so even conditional compilation
  19398. isn't enough. In such an environment, it's a good idea to pass all messages
  19399. through a single centralized location. This makes it easier to enforce message
  19400. presentation standards as well as to redirect error messages: I don't want to
  19401. print to stderr while a GUI is running!
  19402.  
  19403.  
  19404. Designing an Error Manager
  19405.  
  19406.  
  19407. My CAD software is destined for a UNIX environment, so Windows and Macintosh
  19408. resource tools won't help. Also, I'm working alone, so I need a system that's
  19409. easy to develop and use. My design goals are as follows:
  19410. 1. easy replacement of message text
  19411. 2. multiple replacement dictionaries (for partial replacement or subsystems)
  19412. 3. automatic error checking of replacement strings
  19413. 4. 100% reliability and functionality even in the absence of replacement
  19414. dictionaries
  19415. 5. easy re-use of low-level library code that prints error messages
  19416. 6. code readability during maintenance
  19417.  
  19418.  
  19419. General Approach
  19420.  
  19421.  
  19422. I route all error and warning messages through a single error manager. I don't
  19423. try to handle dialogues here, because graphical and text-based interfaces
  19424. often differ substantially, and my software isn't very interactive. However, I
  19425. can replace prompts as well.
  19426. My system stores the default messages in the source code, so they can document
  19427. some of its intent (after all, the user must be able to understand the
  19428. message). Linking the program incorporates the messages into the program
  19429. directly, so there are no resource files to get lost. (However, performing a
  19430. translation requires a message dictionary, which is a separate file containing
  19431. replacement text.)
  19432. Keyed error messages in the source code begin with a '$' followed by an
  19433. alphanumeric message name and a colon. The error manager strips off these keys
  19434. before printing the message (See Listing 1 for an example of a keyed message).
  19435. If the error manager finds a message corresponding to the key name in the
  19436. message dictionary, and the dictionary's parameter order is compatible with
  19437. the original, then the error manager uses the replacement message from the
  19438. dictionary. If it finds any errors in the replacement text, the manager prints
  19439. a warning and uses the original text from the source code.
  19440. I based the system on fprintf because it's very difficult to modify
  19441. streams-based I/O to fit this approach. Every string constant in an output
  19442. statement would require its own key, and formatting could not be changed. If
  19443. you despise fprintf and switched to streams as soon as you learned C++, you're
  19444. probably better off with a resource editor.
  19445.  
  19446.  
  19447. Message Dictionaries
  19448.  
  19449.  
  19450. Message or complaint dictionaries store replacement text for the keyed error
  19451. messages. If a message has a keyed name, the dictionary manager retrieves the
  19452. replacement text and the error manager compares it with the program text. The
  19453. error manager then walks down both message bodies comparing the format
  19454. specifications. If the two disagree, the error manager assumes the program
  19455. text is correct and prints a warning message. The error manager also lists the
  19456. key name so that the user can fix the dictionary text.
  19457. This process demonstrates the advantage of storing the original message in the
  19458. source code: even if the dictionary files are damaged or deleted, the program
  19459. can still print the original message text. The user might complain about
  19460. messages suddenly appearing in English, but I consider this better than
  19461. crashing with no message whatsoever.
  19462. Some fprintf format specifiers are equivalent, or at least read same-size
  19463. objects from the argument list. The function format_specifier in errmgr.cpp
  19464. returns a "canonical" format character for each type. For example,
  19465. format_specifier converts all floating-point numbers to double for fprintf, so
  19466. the 'e', 'f', and 'g' types all map to the same letter. The user can change
  19467. the way values are printed (more digits of precision), but the type ordering
  19468. is fixed in the program.
  19469. Each logical line in a message dictionary contains one message. Newlines are
  19470. quoted with '\' and comment lines begin with '#'. The dictionary format is the
  19471. same as the message format except that the leading '$' is omitted. (This is
  19472. not program text, so you don't need to quote special characters.)
  19473. Note that an implicit newline resides at the end of each message, even if the
  19474. program text does not contain one. If you use this system to retrieve prompt
  19475. text using err_mgr.message, you'll want to strip off this newline or else the
  19476. user's input will be on the next line.
  19477.  
  19478.  
  19479. Message Handlers
  19480.  
  19481.  
  19482. The error manager handles four message types:
  19483.  
  19484. 1) Fatal -- the program prints the message and exits.
  19485. 2) Serious -- the program asks for permission to continue.
  19486. 3) Warning -- the message is printed and the program continues.
  19487. 4) Posting -- the message is simply printed.
  19488. The error manager routes all messages through a handler, which can be replaced
  19489. at run time. For example, the startup code of a GUI would install a graphical
  19490. error reporter (printing to a pop-up box, for example) and the termination
  19491. code would restore the previous handler. All error handlers are based on the
  19492. failure_handler class (see Listing 2).
  19493. The default failure_handler prints to stderr except for the post routine,
  19494. which prints to stdout. The routines themselves are fairly simple (see Listing
  19495. 3). The other routines in failure_handler appear on this month's source code
  19496. disk.
  19497. The class error_mgr defines the error manager. Application code calls the
  19498. member functions of this class's one-and-only instance, err_mgr. err_mgr then
  19499. directs the message to the handler currently defined. (See Listing 4.)
  19500. Handlers stack; define_handler returns the previously defined handler to allow
  19501. a sort of run-time inheritance. For example, an instance of the class
  19502. counting_failure_handler, when created, will install itself in the handler
  19503. stack and then tally each serious error or warning before passing the message
  19504. to the previous handler. The counting_failure_handler will remove itself from
  19505. the handler stack when it is destroyed. Listing 5 shows how
  19506. counting_failure_handler installs and removes itself from the stack.
  19507. The error_mgr class uses counting_failure_handler when reading dictionaries.
  19508. The complaint_dict constructor (Listing 9) parses the dictionary file,
  19509. reporting errors through the warn function. Class complaint_dict is shown in
  19510. Listing 8. If complaint_dict reports any problems the error manager knows the
  19511. dictionary is invalid. This technique gets around the "constructor returns no
  19512. value" problem. Of course, if another failure_handler is stacked on top of a
  19513. counting_failure_handler, errors won't be tallied unless that handler also
  19514. passes its messages downward. Only the topmost handler receives messages.
  19515. Listing 7 shows class ptr_stack, which manages the handler stack.
  19516. A program can use multiple error dictionaries. define_dictionary parses the
  19517. named dictionary and returns 1 if it finds no errors. If define_dictionary
  19518. finds errors the error manager ignores that dictionary. I pass in a filename
  19519. rather than an open file because file descriptors are a precious commodity. To
  19520. replace a message, the dictionary manager reopens the file and rereads the
  19521. message using the stored file offset. If a message appears in more than one
  19522. dictionary the last one defined takes precedence. Listing 6 shows function
  19523. define_dictionary, as well as other member functions of class error_mgr.
  19524. There are two error_mgr routines for each type of error. The second, similar
  19525. to vfprintf, accepts a va_list argument. I found out the hard way that it's
  19526. not a good idea to overload these names; if va_list resolves to (char *) (e.g.
  19527. in Borland C++), then the call
  19528. err_mgr.warn("Can't open %s\n", filename);
  19529. may be routed to the va_list version of the function instead! (ARM section
  19530. 13.2 says "sequences that involve matches with the ellipsis are worse than all
  19531. others.")
  19532. The message function searches for the replacement text for the fmt argument
  19533. and, if found, stores it into the buffer passed along. message then returns a
  19534. pointer to the start of this buffer or within the fmt argument if no such
  19535. message is found so that the result can be used within an fprintf call (e.g. a
  19536. prompt). In this demonstration version, the buffer length is passed in; for
  19537. robustness all routines of this sort should use a buffer object that grows
  19538. when text is added instead.
  19539.  
  19540.  
  19541. Debugging Features
  19542.  
  19543.  
  19544. Last but not least this error manager provides an assertion capability.
  19545. Assertions are always compiled into the code; the macro NDEBUG controls only
  19546. the default value of the assertions_off function. The ASSERT macro is similar
  19547. to assert except that it first checks err_mgr.assertions_off. An assertion
  19548. failure is a serious but not necessarily fatal error in this system.
  19549. err_mgr.set_assert_flag(0) sets err_mgr.assertions_off, short-circuiting all
  19550. assertion evaluations. Since it's an inline function returning the value of a
  19551. static member value, set_assert_flag doesn't slow down assertion evaluation.
  19552. In the worst case assertions can slow execution up to 20%, so users might not
  19553. want assertions on unless the program is prone to crashes.
  19554. Note that all of the private variables are all static integers or pointers. In
  19555. errmgr.cpp these variables are all initialized to zero, because the ARM states
  19556. that assignments to zero are performed first. Global constructors are called
  19557. in an implementation-specific order, so if an error occurs in a constructor
  19558. that executes prior to the err_mgr constructor, err_mgr must set itself up.
  19559. Thus every routine in errmgr.cpp, including the constructor, calls setup
  19560. first, directly or indirectly.
  19561. setup allocates all of the non-simple variables off the heap to ensure they
  19562. are properly initialized as well.
  19563.  
  19564.  
  19565. Automated Message Extraction
  19566.  
  19567.  
  19568. Once you've completed your application, you'll want to build the message
  19569. dictionaries automatically. This month's code disk contains source code for a
  19570. scanner written for flex, the fast replacement for the UNIX lex program. The
  19571. scanner searches for string constants (merging adjacent string constants, of
  19572. course) that appear to be keyed error messages.
  19573. I didn't try to write a C++ parser, or even to locate all err_mgr calls,
  19574. because some of my mid-level utility routines implement their own error
  19575. logging systems on top of err_mgr. For example, a parser will often print the
  19576. language context in addition to the error message. Thus I'd have to analyze
  19577. the program to determine that calls such as
  19578. lexer->complain("$illegal_char:" "Illegal character. \n");
  19579. were in fact calls to err_mgr. At worst the scanner will find a few extra
  19580. "messages." Note that the scanner won't find keyed messages constructed at run
  19581. time; they would be difficult for users to translate anyway.
  19582. The scanner in its current form requires flex because it uses some features
  19583. that only flex provides. [A public-domain version of flex that can be compiled
  19584. under both MS-DOS and UNIX is available from the C User's Group Library. See
  19585. ordering information at the end of this article. --mb] I've included the
  19586. output C file as well so that you can at least compile the program. The code
  19587. that actually generates the dictionaries is in findmsgs.c, so if you want to
  19588. change the format of the messages or dictionaries you won't need to edit the
  19589. scanner itself.
  19590.  
  19591.  
  19592. Message Database Management
  19593.  
  19594.  
  19595. With every release of your software you will probably need to rebuild the
  19596. message dictionaries, because you will most likely add new messages and modify
  19597. old messages. If your customers (or a group within your company) have
  19598. translated all of the messages into another language, you'll need to help them
  19599. with the update. I recommend building a new set of dictionaries for the
  19600. release, then comparing them message-by-message with the previous release.
  19601. This process will result in a list of messages added or changed by the
  19602. programmer. Customers must then write or edit translations for these messages
  19603. into another language, you'll need to help them with the update. I recommend
  19604. building a new set of dictionaries of the release then comparing them
  19605. message-by-message with the previous release. This process will result in a
  19606. list of messages added or changed by the programmer. Customers must then write
  19607. or edit translations for these messages.
  19608. If your documentation or support groups are modifying message text (for
  19609. example, to ensure consistent wording), then you'll need to compare the source
  19610. code messages in the current release with the edited dictionaries. This step
  19611. will give you a list of messages that the support groups have modified, to
  19612. merge with the programmers' modifications.
  19613.  
  19614.  
  19615. Limitations
  19616.  
  19617.  
  19618. Naturally, any system based on fprintf is subject to parameter typing
  19619. problems. Without a full C++ parser, plus program knowledge to determine which
  19620. calls will be routed to the error manager, you can't guarantee against
  19621. printing occasional garbage. Streams have an obvious advantage here, but again
  19622. text replacement is a problem with them.
  19623. Currently, you can't edit messages in a way that rearranges the order of the
  19624. parameters. At most you can rearrange the text around the parameters. You
  19625. would have to define a whole new fprintf-styleformat and write routines to
  19626. extract the parameters from the argument list in their original order, then
  19627. shuffle them to meet the new requirements. It's not a simple task, so I didn't
  19628. even try.
  19629. My CAD software is rather large, straining the limits of MSDOS, so I don't
  19630. keep the replacement text in memory. You could modify the dictionary manager
  19631. very easily to keep the message text around so that it wouldn't have to
  19632. re-open the file every time a message was requested.
  19633.  
  19634.  
  19635. Limitations in This Version
  19636.  
  19637.  
  19638. My production error manager uses a managed buffer that grows in length
  19639. automatically as text is added to it. This demonstration implementation uses
  19640. fixed-length (usually 512 characters) buffers instead. For safety you should
  19641. use some kind of expanding buffer. I've marked the source code to show where
  19642. the fixedlength buffers are used. Look for variables named linelen and the
  19643. associated comments.
  19644. For speed, keyword lookup should use a hash table. I've used a singly-linked
  19645. list here for simplicity.
  19646. This month's code disk contains the following:
  19647. fully commented source for all modules and the C++ scanner
  19648. test programs for error_mgr, complaint_dict, ASSERT
  19649. makefiles for Zortech C++ 3.l, Symantec C++ 6.00, Borland C++ 4.00, and UNIX
  19650. (the latter untested)
  19651.  
  19652. findmsgs.exe (the scanner) compiled using Zortech C++
  19653. All code described in this article is placed in the public domain. You may use
  19654. it as you wish.
  19655.  
  19656.  
  19657. Acknowledgements
  19658.  
  19659.  
  19660. The flex scanner is based on one that Tony Sanders (once upon a time at
  19661. cs.utexas.edu!ibmaus!auschs!sanders.austin. ibm.com!sanders) wrote in 1990.
  19662. I've extended it to support floating-point numbers and quoted characters
  19663. within string constants.
  19664.  
  19665.  
  19666. Obtaining a Version of Flex
  19667.  
  19668.  
  19669. Daniel R. Haney (MA) has ported flex to MS-DOS. Flex is available in CUJ
  19670. Library volume 290. The disk contains a complete set of source code,
  19671. documentation, a makefile, and a word count program as an example. Haney's
  19672. implementation of flex can be compiled under MSDOS and UNIX. An OS/2
  19673. executable is included.
  19674. Also check out Flex++, CUJ volume 405. For more information, contact:
  19675. R&D Publications
  19676. 1601 W 23rd, Suite 200
  19677. Lawrence, KS 66046
  19678. (913)-841-1631. FAX: (913)-841-2624
  19679. e-mail: michelle@rdpub.com.
  19680.  
  19681. Listing 1 A keyed error message passed to the error manager
  19682. err_mgr.fail("$edif_keyword: EDIF production %s"
  19683. "resolved to unknown keyword %s\n",
  19684. prod->keyword(), newkeyword);
  19685.  
  19686.  
  19687. Listing 2 Definition of class failure_handler
  19688. /* message handler: fail() is for fatal errors, */
  19689. /* error() is for serious errors, warn() is for */
  19690. /* warning messages to stderr, and post() is for */
  19691. /* ordinary messages to stdout. */
  19692.  
  19693. class failure_handler {
  19694. public:
  19695. failure_handler(void) {}
  19696. virtual void fail(const char *fmt,va_list ap);
  19697. virtual void error(const char *fmt,va_list ap);
  19698. virtual void warn(const char *fmt,va_list ap);
  19699. virtual void post(const char *fmt,va_list ap);
  19700. private:
  19701. /* unimplemented: */
  19702. failure_handler(const failure_handler &other);
  19703. failure_handler
  19704. &operator =(const failure_handler &other);
  19705. };
  19706. // End of File
  19707.  
  19708.  
  19709. Listing 3 A sample member function of class failure_handler
  19710. void failure_handler::error(const char *fmt,va_list ap)
  19711. {
  19712. fputs("ERROR: ",stderr);
  19713. vfprintf(stderr,fmt,ap);
  19714. if (!ok_to_continue())
  19715. exit(EXIT_FAILURE);
  19716. }
  19717. // End of File
  19718.  
  19719.  
  19720.  
  19721. Listing 4 Definition of class error_mgr
  19722. /* errmgr.hpp fragment */
  19723.  
  19724. class error_mgr {
  19725. public:
  19726. error_mgr(void);
  19727. ~error_mgr(void);
  19728. int define_dictionary(const char *filename);
  19729.  
  19730. /* returns handler just hidden */
  19731. failure_handler *define_handler(
  19732. failure_handler *new_handler);
  19733. /* returns handler just popped */
  19734. failure_handler *restore_handler(void);
  19735.  
  19736. void fail(const char *fmt ,...);
  19737. void vfail(const char *fmt,va_list ap);
  19738. void error(const char *fmt ,...);
  19739. void verror(const char *fmt,va_list ap);
  19740. void warn(const char *fmt ,... );
  19741. void vwarn(const char *fmt,va_list ap);
  19742. void post(const char *fmt ,... );
  19743. void vpost(const char *fmt,va_list ap);
  19744.  
  19745. const char *message(const char *fmt,
  19746. char *msg_line,int len);
  19747.  
  19748. int set_assert_flag(int asserts_on);
  19749. static int assertions_off(void)
  19750. { return asserts_off; }
  19751. int assert_failed(
  19752. const char *exp,const char *fname,
  19753. unsigned linenum);
  19754. private:
  19755. void setup(void);
  19756. static failure_handler *curr_handler,
  19757. *default_handler;
  19758. static int is_setup,asserts_off;
  19759. static error_dict_list *error_dicts;
  19760. static ptr_stack *handler_stack;
  19761. int find_replacement(
  19762. const char *key,char *msg_line,
  19763. int linelen);
  19764. /* unimplemented: */
  19765. error_mgr(const error_mgr &other);
  19766. error_mgr &operator =(const error_mgr &other);
  19767. };
  19768.  
  19769. extern error_mgr err_mgr;
  19770.  
  19771. #define ASSERT(e) \
  19772. ((void)(err_mgr.assertions_off() \
  19773. (e) \
  19774. err_mgr.assert_failed(#e,_FILE_,_LINE_)))
  19775. // End of File
  19776.  
  19777.  
  19778. Listing 5 Member functions of class counting_failure_handler
  19779.  
  19780. counting_failure_handler::
  19781. counting_failure_handler(void)
  19782. {
  19783. /* install ourselves as the current handler */
  19784. /* when we are created. */
  19785.  
  19786. error_logged = warn_logged = 0L;
  19787. prev_handler = err_mgr.define_handler(this);
  19788. }
  19789.  
  19790. counting_failure_handler::
  19791. ~counting_failure_handler(void)
  19792. {
  19793. /* unlink ourselves from the handler chain */
  19794. /* when we are destroyed. */
  19795.  
  19796. failure_handler *top = err_mgr.restore_handler();
  19797. /* no other handler should be stacked over us! */
  19798. ASSERT(top == this);
  19799. }
  19800.  
  19801. void counting_failure_handler::error(const char *fmt,
  19802. va_list ap)
  19803. {
  19804. ++error_logged; /* tally problems */
  19805. prev_handler->error(fmt,ap); /* pass message on */
  19806. }
  19807. // End of File
  19808.  
  19809.  
  19810. Listing 6 Member functions of class error_mgr
  19811. /* errmgr.cpp - error manager */
  19812. /* has been trimmed to fit; see the monthly code */
  19813. /* disk for the complete source. */
  19814.  
  19815. #include <stdio.h>
  19816. #include <ctype.h>
  19817. #include <stdlib.h>
  19818. #include <stdarg.h>
  19819. #include <string.h>
  19820. #include "complain.hpp"
  19821. #include "utils.hpp"
  19822. #include "c_failur.hpp"
  19823. #include "errmgr.hpp"
  19824.  
  19825. /* an element in the list of complaint dictionaries: */
  19826.  
  19827. class error_dict_list {
  19828. public:
  19829. error_dict_list(error_dict_list *curr,
  19830. complaint_dict *new_dict);
  19831. error_dict_list *prev;
  19832. complaint_dict *dict;
  19833. };
  19834.  
  19835. error_mgr err_mgr;
  19836.  
  19837. /* is_set_up is set when err_mgr is initialized. no */
  19838. /* other error manager can be active. */
  19839.  
  19840.  
  19841. int error_mgr::is_set_up = 0);
  19842.  
  19843. /* asserts_off is 0 when assertions are to be checked. */
  19844.  
  19845. #ifndef NDEBUG
  19846. int error_mgr::asserts_off = 0;
  19847. #else
  19848. int error_mgr::asserts_off = 1;
  19849. #endif
  19850.  
  19851. failure_handler *error_mgr::curr_handler = 0,
  19852. *error_mgr::default_handler = 0;
  19853. error_dict_list *error_mgr::error_dicts = 0;
  19854. ptr_stack *error_mgr::handler_stack = 0;
  19855.  
  19856. static char line[512]; /* should be managed buffer */
  19857.  
  19858. static int proper_format(const char *pgm_text,
  19859. const char *user_text);
  19860. static char format_specifier(const char **fmt);
  19861.  
  19862. error_mgr::error_mgr(void) /* constructor */
  19863. {
  19864. setup();
  19865. }
  19866.  
  19867. error_mgr::~error_mgr(void) /* destructor */
  19868. {
  19869. delete default_handler;
  19870. delete handler_stack;
  19871. }
  19872.  
  19873. void error_mgr::setup(void)
  19874. {
  19875. /* create default handler and empty dictionary */
  19876. /* list, allocate stack for failure_handlers, */
  19877. /* make sure no other error_mgr is present. */
  19878. if (this != &err_mgr) /* just in case... */
  19879. err_mgr.fail("Duplicate error_mgr defined.\n");
  19880. if (is_set_up)
  19881. return;
  19882. is_set_up = 1;
  19883. curr_handler = default_handler =
  19884. new failure_handler;
  19885. handler_stack = new ptr_stack;
  19886. }
  19887.  
  19888. int error mgr::define_dictionary(const char *filename)
  19889. {
  19890. /* add a new dictionary to the list. it's */
  19891. /* ignored if any errors are found. */
  19892. counting_failure_handler *our_handler;
  19893. complaint_dict *new_dict;
  19894.  
  19895. setup();
  19896. /* we count errors in the file with our_handler. */
  19897. our_handler = new counting_failure_handler;
  19898. new_dict = new complaint_dict(filename);
  19899.  
  19900. if (our_handler->errors_logged() 
  19901. our_handler->warns_logged()) {
  19902. delete new_dict;
  19903. warn("$bad_error_dict: dictionary file \"%s\" "
  19904. "has errors - will not be used\n",
  19905. filename);
  19906. delete our_handler;
  19907. return 0;
  19908. }
  19909. error_dicts = new error_dict_list(
  19910. error_dicts,new_dict);
  19911. delete our_handler;
  19912. return 1;
  19913. }
  19914.  
  19915. failure_handler *error_mgr::define_handler(
  19916. failure_handler *new_handler)
  19917. {
  19918. /* install a new error handler, pushing the */
  19919. /* previous handler onto a stack. */
  19920.  
  19921. failure_handler *old_handler;
  19922.  
  19923. setup();
  19924. handler_stack->push(curr_handler);
  19925. old_handler = curr_handler;
  19926. if (new_handler == 0) /* bomb-proof */
  19927. curr_handler = default_handler;
  19928. else
  19929. curr_handler = new_handler;
  19930. return old_handler;
  19931. }
  19932.  
  19933. failure_handler *error_mgr::restore_handler(void)
  19934. {
  19935. /* return to the previous error handler. */
  19936. failure_handler *old_handler;
  19937.  
  19938. setup();
  19939. old_handler = curr_handler;
  19940. curr_handler = (failure_handler *)
  19941. (handler_stack->pop());
  19942. if (curr_handler == 0)
  19943. curr_handler = default_handler;
  19944. return old_handler;
  19945. }
  19946.  
  19947. const char *error_mgr::message(
  19948. const char *fmt,char *msg_line,
  19949. int linelen)
  19950. {
  19951. /* search for the replacement text, writing */
  19952. /* it into msg_line if found. return a pointer */
  19953. /* to the start of the message, ready to print. */
  19954. const char *new_fmt,*key,*keystart;
  19955. char *key_alloc,*buf;
  19956. int keylen;
  19957.  
  19958. /* this routine calls setup() for fail(), */
  19959.  
  19960. /* vfail(), etc. */
  19961. setup();
  19962. ASSERT(fmt != msg_line);
  19963. msg_line[0] = '\0';
  19964. if (*fmt ! = '$') { /* not keyed? */
  19965. strncpy(msg_line,fmt,linelen);
  19966. msg_line[linelen - 1] = '\0';
  19967. return msg_line;
  19968. }
  19969.  
  19970. /* extract the key. */
  19971. key = ++fmt; /* skip '$' */
  19972. key += skipblanks(key);
  19973. keylen = skip_ident(key); /* fetch key name */
  19974. new_fmt = key + keylen; /* skip key name */
  19975. new_fmt += skipblanks(key); /* skip to ':' */
  19976.  
  19977. /* error text here is not replaceable - */
  19978. /* recursive calls would be dangerous. */
  19979. if (keylen == 0 ll *new_fmt != ':') {
  19980. sprintf(msg_line,"Keyed error message format"
  19981. "string is malformed\n%s",fmt);
  19982. }
  19983. else {
  19984. key_alloc = newstring(key,keylen);
  19985. ++new_fmt; /* skip ':' */
  19986. new_fmt += skipblanks (new_fmt);
  19987. if (find_replacement(key_alloc,msg_line,
  19988. linelen)) {
  19989. if (!proper_format(new_fmt,msg_line)) {
  19990. /* invalid; dictionary text left at */
  19991. /* front of resulting error message. */
  19992. buf = msg_line + strlen(msg_line);
  19993. sprintf(buf,"Replacement text for "
  19994. "keyed error message is "
  19995. "malformed\n%s", fmt);
  19996. }
  19997. }
  19998. else { /* use program version */
  19999. strncpy(msg_line,new_fmt,linelen);
  20000. msg_line[linelen - 1] = '\0';
  20001. }
  20002. free(key_alloc);
  20003. }
  20004. return msg_line;
  20005. }
  20006.  
  20007. int error_mgr::find_replacement(
  20008. const char *key,char *msg_line,
  20009. int linelen)
  20010. {
  20011. /* search for message replacement. returns 1 */
  20012. /* if found, 0 if not. */
  20013. error_dict_list *curr_dict;
  20014.  
  20015. for (curr_dict = error_dicts; curr_dict != 0;
  20016. curr_dict = curr_dict->prev)
  20017. if (curr_dict->dict->key_defined(key))
  20018. break;
  20019.  
  20020. return curr_dict != 0 &&
  20021. curr_dict->dict->complaint_text(
  20022. key,msg_line,linelen);
  20023. }
  20024.  
  20025. static int proper_format(const char *pgm_text,
  20026. const char *user_text)
  20027. {
  20028. /* return 1 if the printf() format specifiers */
  20029. /* in user_text match the ones in pgm_text. */
  20030. char pgm_fmt,user_fmt;
  20031.  
  20032. do {
  20033. pgm_fmt = format_specifier(&pgm_text);
  20034. user_fmt = format_specifier(&user_text);
  20035. if (pgm_fmt!= user_fmt)
  20036. return 0;
  20037. } while (*pgm_text *user_text);
  20038. return 1;
  20039. }
  20040.  
  20041. static char format_specifier(const char **fmt)
  20042. {
  20043. /* skip to the next '%' specifier (if any) */
  20044. /* and return a unique letter indicating its */
  20045. /* type. advances *fmt past the specifier. */
  20046. const char *s : *fmt; /* *s vs. **fmt */
  20047. char c;
  20048.  
  20049. for (;;) { /* skip to '%' */
  20050. while (*s && *s != '%')
  20051. ++s;
  20052. if (*s == '\0')
  20053. break;
  20054. ++s; /* skip '%' */
  20055. if (*s == '%') /* "%%" prints '%' */
  20056. ++s; /* not specifier */
  20057. else
  20058. break;
  20059. }
  20060. if (*s == '\0') {
  20061. *fmt = s; /* update caller var */
  20062. return '\0';
  20063. }
  20064.  
  20065. /* skip to the specifier letter. */
  20066. while (*s && !isspace(*s) && !isalpha(*s))
  20067. ++s;
  20068. if (!isalpha(*s)) {
  20069. *fmt = s; /* update caller var */
  20070. return '\0'; /* bad specifier */
  20071. }
  20072.  
  20073. /* any 'l' is assumed to be long. */
  20074. if (*s == 'l' && isalpha(*(s + 1))) {
  20075. *fmt = s + 2; /* skip l, specifier */
  20076. return '1';
  20077. }
  20078.  
  20079.  
  20080. /* map the specifier into a canonical value. */
  20081. c = tolower(*s);
  20082. *fmt = s + 1; /* update caller var */
  20083. switch(c) {
  20084. case 'e': case 'f': case 'g':
  20085. return 'e'; /* floating point */
  20086. case 'o': case 'u': case 'x':
  20087. case 'd': case 'i': case 'b':
  20088. return 'd'; /* int */
  20089. default: /* all others map */
  20090. return c; /* to themselves */
  20091. } /* end of switch(c) */
  20092. /* NOTREACHED */
  20093. }
  20094.  
  20095. void error_mgr::fail(const char *fmt,...)
  20096. {
  20097. /* print the message and exit. */
  20098. va_list ap;
  20099.  
  20100. va_start(ap,fmt);
  20101. vfail(fmt,ap); /* won't return */
  20102. /* NOTREACHED */
  20103. va_end(ap);
  20104. }
  20105.  
  20106. void error_mgr::vfail(const char *fmt,va_list ap)
  20107. {
  20108. /* fail() with a va_list already built. if */
  20109. /* the handler doesn't exit we force it. */
  20110. fmt = message(fmt,line,sizeof(line));
  20111. curr_handler->fail(fmt,ap);
  20112. exit(EXIT_FAILURE); /* just in case! */
  20113. }
  20114.  
  20115. void error_mgr::error(const char *fmt,...)
  20116. {
  20117. /* print the message and ask for permission to */
  20118. /* continue. */
  20119. va_list ap;
  20120.  
  20121. va_start(ap,fmt);
  20122. verror(fmt,ap);
  20123. va_end(ap);
  20124. }
  20125.  
  20126. void error_mgr::verror(const char *fmt,va_list ap)
  20127. {
  20128. /* error() with a va_list already built. */
  20129. /* we replace the message if possible. */
  20130. fmt = message(fmt,line,sizeof(line));
  20131. curr_handler->error(fmt,ap);
  20132. }
  20133.  
  20134. int error_mgr::set_assert_flag(int asserts_on)
  20135. {
  20136. /* enable or disable assertions at run time. */
  20137. int retval = asserts_off;
  20138.  
  20139.  
  20140. /* set to opposite state because we want to */
  20141. /* short-circuit assertion evaluation. */
  20142. asserts_off = !asserts_on;
  20143. return !retval;
  20144. }
  20145.  
  20146. int error_mgr::assert_failed(
  20147. const char *exp,const char *fname,
  20148. unsigned linenum)
  20149. {
  20150. /* an assertion has failed. */
  20151. error("Assertion failed - file %s, line %u:\n%s\n",
  20152. fname,linenum,exp);
  20153. return 1; /* needed for macro */
  20154. }
  20155.  
  20156. // End of File
  20157.  
  20158.  
  20159. Listing 7 utils.hpp - text and pointer utilities. utils.cpp is on the monthly
  20160. source code disk
  20161. #ifndef UTILS_HPP
  20162. #define UTILS_HPP
  20163.  
  20164. #include <stdio.h>
  20165.  
  20166. int skipblanks(const char *s); /* returns length */
  20167. int skip_ident(const char *s); /* returns length */
  20168.  
  20169. /* allocates with malloc. copies only "len" chars */
  20170. /* unless len == 0; then copies entire string */
  20171.  
  20172. char *newstring(const char *s,int len = );
  20173.  
  20174. /* read "logical" line, quoting newlines if the */
  20175. /* character before them is '\\' */
  20176.  
  20177. void read_continued_line(FILE *f,char *line,
  20178. int linelen);
  20179.  
  20180. /* non-template pointer stack. doesn't delete */
  20181. /* anything when deleted. */
  20182.  
  20183. class ptr_stack {
  20184. public:
  20185. ptr_stack(void);
  20186. ptr_stack(const ptr_stack &other);
  20187. ptr_stack &operator =(const ptr_stack &other);
  20188. ~ptr_stack(void);
  20189. void *push(void *op);
  20190. long elem_count(void); /* 0L if empty */
  20191. void *top(void) const; /* doesn't pop */
  20192. void * pop(void); /* 0 if empty */
  20193. private:
  20194. long size, top_idx;
  20195. void **elems;
  20196. }; /* end of class ptr_stack */
  20197.  
  20198. #endif /* UTILS_HPP */
  20199. // End of File
  20200.  
  20201.  
  20202.  
  20203. Listing 8 Definition of class complaint_dict
  20204. /* complain.hpp - message dictionary handler */
  20205.  
  20206. #ifndef COMPLAIN_HPP
  20207. #define COMPLAIN_HPP
  20208.  
  20209. class complain_ptr; /* in complain.cpp */
  20210.  
  20211. class complaint_dict {
  20212. public:
  20213. complaint_dict(const char *filename);
  20214. ~complaint_dict(void);
  20215. int key_defined(const char *name) const;
  20216. int complaint_text(const char *name,
  20217. char *line,
  20218. int linelen) const;
  20219. const char *filename(void) const
  20220. { return_filename; }
  20221. private:
  20222. char *_filename;
  20223. complain_ptr *complain_table;
  20224. /* unimplemented: */
  20225. complaint_dict(const complaint_dict &other);
  20226. complaint_dict &operator
  20227. =(const complaint_dict &other);
  20228. }; /* end of class complaint_dict */
  20229.  
  20230. #endif /* COMPLAIN_HPP */
  20231. // End of File
  20232.  
  20233.  
  20234. Listing 9 Member functions of class complaint_dict
  20235. /* complain.cpp - message dictionary manager */
  20236.  
  20237. #include <stdio.h>
  20238. #ifdef __ZTC______LINEEND____
  20239. #include <io.h> /* fseek() */
  20240. #endif
  20241. #include <stdlib.h>
  20242. #include <string.h>
  20243. #include "utils.hpp"
  20244. #include "errmgr.hpp"
  20245. #include "complain.hpp"
  20246.  
  20247. /* a singly linked list of keys - should use hash */
  20248. /* table for speed */
  20249. class complain_ptr {
  20250. public:
  20251. complain_ptr(char *name,long offset,
  20252. complain_ptr *head);
  20253. ~complain_ptr(void);
  20254. const char *errname(void) const
  20255. { return_errname; }
  20256. int complaint_text(const char *filename,
  20257. char *line,int linelen);
  20258. complain_ptr *next;
  20259. private:
  20260.  
  20261. char * _errname;
  20262. long foffset;
  20263. };
  20264.  
  20265. /************** class complaint_dict ***************/
  20266.  
  20267. static complain_ptr *read_complaint_file(
  20268. const char *filename);
  20269.  
  20270. complaint_dict::complaint_dict(const char *filename)
  20271. {
  20272. /* constructor - remember file name, read all */
  20273. /* messages and store offsets */
  20274. _filename= newstring(filename);
  20275. complain_table = read_complaint_file(_filename);
  20276. }
  20277.  
  20278. complaint_dict: :~complaint_dict (void)
  20279. {
  20280. /* cleanup - delete all of the offset records */
  20281. /* in the table. */
  20282. complain_ptr *keyptr;
  20283.  
  20284. free(_filename);
  20285. while ((keyptr= complain_table) != 0) {
  20286. complain_table = complain_table->next;
  20287. delete keyptr;
  20288. }
  20289. }
  20290.  
  20291. static complain_ptr *key(complain_ptr *complain_table,
  20292. const char *name)
  20293. {
  20294. /* look for the key in the list of complain_ptr */
  20295. /* objects. returns 0 if not found. */
  20296. while (complain_table != 0 &&
  20297. strcmp(name,complain_table->errname()))
  20298. complain_table= complain_table->next;
  20299. return complain_table;
  20300. }
  20301.  
  20302. int complaint_dict::key_defined(const char *name) const
  20303. {
  20304. /* return 1 if the key is defined here. */
  20305. return key(complain_table,name) != 0;
  20306. }
  20307.  
  20308. int complaint_dict::complaint_text(const char *name,
  20309. char *line,
  20310. int linelen) const
  20311. {
  20312. /* retrieve the text for the named message and */
  20313. /* store it in the buffer. returns 0 on failure. */
  20314. complain_ptr *fmt;
  20315.  
  20316. fmt = key(complain_table,name);
  20317. if (fmt == 0) {
  20318. sprintf(line,"Error key \"%s\" not found\n",
  20319. key);
  20320.  
  20321. return 0; /* failed */
  20322. }
  20323. if (fmt->complaint_text(_filename,line,linelen)) {
  20324. sprintf(line,"Unable to re-read text for"
  20325. "error key \"%s\"\n",key);
  20326. return 0; /* failed */
  20327. }
  20328.  
  20329. return 1; /* all OK */
  20330.  
  20331. } /* end of complaint_dict::complaint_text() */
  20332.  
  20333. /************* local utility routines **************/
  20334.  
  20335. static complain_ptr *read_complaint_file(
  20336. const char *filename)
  20337. {
  20338. /* read all of the error keys in the complaint */
  20339. /* file. */
  20340. FILE *complaintfile;
  20341. char line[512];
  20342. long thisoffset;
  20343. char *keyname,*p,*name;
  20344. int all_ok = 1;
  20345. complain_ptr *keyptr,*complain_table = 0;
  20346.  
  20347. if ((complaintfile = fopen(filename,"r")) == 0) {
  20348. err_mgr.error("Unable to read error text"
  20349. "file %s\n",filename);
  20350. return 0; /* OK to continue */
  20351. }
  20352.  
  20353. for (;;) { /* exit from within */
  20354. /* remember the start of each line. */
  20355. thisoffset = ftell(complaintfile);
  20356. read_continued_line(complaintfile,line,
  20357. sizeof(line));
  20358. if (strlen(line) == 0) /* EOF? done*/
  20359.  
  20360. break;
  20361.  
  20362. /* skip blank lines and comments. */
  20363. keyname = p = line + skipblanks(line);
  20364. if (!*p *p == '\n' *P == '#')
  20365. continue;
  20366. p += skip_ident(p); /* get key name */
  20367. if (p == keyname) {
  20368. err_mgr.warn("Missing error key in error"
  20369. text file \"%s\":\n%s",
  20370. filename,line);
  20371. all_ok = 0;
  20372. continue;
  20373. }
  20374.  
  20375. name = newstring(keyname,p - keyname);
  20376. if (key(complain_table,name) != 0) {
  20377. err_mgr.warn("Duplicate error key in"
  20378. "error text file \"%s\":\n%s",
  20379. filename,line);
  20380.  
  20381. free(name);
  20382. all_ok = 0;
  20383. continue;
  20384. }
  20385. p += skipblanks(p);
  20386. if (*p != ':') {
  20387. err_mgr.warn("Missing ':' in error text"
  20388. "file \"%s\":\n%s",
  20389. filename,line);
  20390. free(name);
  20391.  
  20392. all_ok = 0;
  20393. continue;
  20394. }
  20395.  
  20396. /* everything looks good - remember key. */
  20397. keyptr = new complain_ptr(name,thisoffset,
  20398. complain_table);
  20399. complain_table = keyptr;
  20400. }
  20401.  
  20402. /* if problems were found ignore entire file. */
  20403. fclose(complaintfile);
  20404. if (!all_ok) {
  20405. while ((keyptr = complain_table) != 0) {
  20406. complain table = complain_table->next;
  20407. delete keyptr;
  20408. }
  20409. return 0;
  20410. }
  20411. return complain_table;
  20412. }
  20413.  
  20414. /**************** class complain_ptr ****************/
  20415.  
  20416. complain_ptr:: complain_ptr(char *name, long offset,
  20417. complain_ptr *head)
  20418. { /* constructor */
  20419. /* add the new complain_ptr to the front of */
  20420. /* the list. name is allocated for us; the */
  20421. /* destructor must free it. */
  20422. _errname = name;
  20423. foffset = offset;
  20424. next = head;
  20425. }
  20426.  
  20427. complain_ptr::~complain_ptr(void) /* destructor */
  20428. {
  20429. /* our caller cleans up the chain in next. */
  20430. free(_errname);
  20431. }
  20432.  
  20433. int complain_ptr::complaint text(
  20434. const char *filename,
  20435. char *line,int linelen)
  20436. {
  20437. /* re-read the message text for the key. */
  20438. /* returns 1 on error (file has disappeared or */
  20439. /* been edited). */
  20440.  
  20441. FILE *complaintfile;
  20442. char *keyname,*p;
  20443. int newlen;
  20444.  
  20445. line[0] = '\0'; /* clear old text */
  20446. if ((complaintfile = fopen(filename,"r")) == 0)
  20447. return 1;
  20448. if (fseek(complaintfile,foffset,SEEK_SET)) {
  20449. fclose(complaintfile);
  20450. return 1;
  20451. }
  20452. read_continued_line(complaintfile,line,linelen);
  20453. fclose(complaintfile);
  20454.  
  20455. /* make sure we still have the same key! (file */
  20456. /* may have been edited) */
  20457. keyname = p = line + skipblanks(line);
  20458. p += skip_ident(p);
  20459. if (p == keyname 
  20460. strncmp(keyname, errname,strlen(_errname)))
  20461. return 1;
  20462. p += skipblanks(p);
  20463.  
  20464. if (*p != ':')
  20465. return 1;
  20466. ++p; /* skip ':', */
  20467. p += skipblanks(p); /* leading blanks */
  20468.  
  20469. /* shift everything over to cover the key. */
  20470. newlen = strlen(p);
  20471. memmove(line,p,newlen + 1);
  20472. return 0; /* all OK */
  20473. }
  20474. // End of File
  20475.  
  20476.  
  20477.  
  20478.  
  20479.  
  20480.  
  20481.  
  20482.  
  20483.  
  20484.  
  20485.  
  20486.  
  20487.  
  20488.  
  20489.  
  20490.  
  20491.  
  20492.  
  20493.  
  20494.  
  20495.  
  20496.  
  20497.  
  20498.  
  20499.  
  20500.  
  20501.  
  20502.  
  20503.  
  20504. Designing a Cross-Platform GUI
  20505.  
  20506.  
  20507. Laszlo Zeke
  20508.  
  20509.  
  20510. Laszlo Zeke has been supervising, designing, and developing GUI-based
  20511. applications over 12 years. He has an M.A. in mathematics. He resides in
  20512. Herndon, Virginia, and is the Director of Kernel Product Engineering for
  20513. INCODE Corporation. Laszlo can be reached via e-mail at incode@netcom.com or
  20514. via CompuServe at 76667,1776.
  20515.  
  20516.  
  20517.  
  20518.  
  20519. Introduction
  20520.  
  20521.  
  20522. Sooner or later you're bound to meet a customer who likes your GUI-based
  20523. application but wants it to run in his own GUI environment, different from
  20524. yours. You may wish to accomodate customers such as this, but find it too
  20525. expensive to maintain different code sets on different platforms.
  20526. A common solution is to package the GUI-dependent pieces into an object
  20527. library; the application will then use the objects implemented in this GUI
  20528. library only, instead of the native GUI interfaces.
  20529. You have two basic choices in implementing this type of object library: either
  20530. purchase a third-party multi-platform toolkit (there are plenty), or develop
  20531. your own, using the native APIs. Both approaches have their merits, as well as
  20532. proponents.
  20533. In this article I demonstrate the latter approach by implementing a GUI
  20534. library using the native APIs of Windows/NT, OS/2, and X-Windows. This is just
  20535. a sample library since the source code of a full-blown, commercial-strength
  20536. library would run well over 10,000 lines -- obviously beyond the space
  20537. available here.
  20538. This article presents the class design for the platform-independent portion of
  20539. the GUI, plus a sample application. I also briefly describe the
  20540. platform-specific parts of the code. The platform-dependent code is included
  20541. on this month's code disk, and is available from the on-line sources listed on
  20542. page 3.
  20543.  
  20544.  
  20545. Overall Library Features
  20546.  
  20547.  
  20548. An application that uses this library will have the following capabilities:
  20549. It can have a number of windows.
  20550. Each window may have a different background color and frame decorations.
  20551. Each window can have a number of "children:" static text elements, buttons,
  20552. entry fields, lines, and ellipses.
  20553. If a window is resized, its children are automatically resized.
  20554. The activation of every button and entry field causes the execution of an
  20555. application-defined function.
  20556.  
  20557.  
  20558. Library Design
  20559.  
  20560.  
  20561. Ideally, I might have based my design solely on its ability to meet a given
  20562. set of criteria, such as the features presented above. In reality, any design
  20563. will be influenced by the tools at hand, in this case, the compilers available
  20564. for the various platforms.
  20565.  
  20566.  
  20567. Tools
  20568.  
  20569.  
  20570. I selected the following tools for these three target environments:
  20571. Windows NT/Window 3.1: Visual C++, plus Win32s 1.2 (Win32s 1.2 is required to
  20572. run applications built with this library under Windows 3.1)
  20573. OS/2: Borland C++ 1.5
  20574. AIX: Motif 1.2, X11R5: XL C++, the C++ compiler for the AIX/RISC-6000 machines
  20575. (Note: I am not suggesting that these tools are better than others; they are
  20576. simply the tools I'm familiar with.)
  20577. The code presented in this article will compile and run on each of these
  20578. platforms. Since error checking would double the size of the code, I have left
  20579. it out entirely.
  20580.  
  20581.  
  20582. The General Application Framework
  20583.  
  20584.  
  20585. The library tries to make use of the common elements in the native toolkits.
  20586. When there is nothing in common, the library tries to bridge the differences
  20587. with the least possible effort.
  20588. Although each toolkit is different, any application that uses such a toolkit
  20589. will follow the same general pattern, or framework:
  20590. 1. some initialization of the toolkit
  20591. 2. creation of one or more windows along with their children
  20592. 3. processing messages in a loop that retrieves and dispatches different
  20593. messages to the different windows and to their children based upon the user's
  20594. actions
  20595.  
  20596. 4. termination with some possible cleanup
  20597. To encapsulate this initialize-message loop-cleanup model, I introduce a
  20598. class, GUI_APPLICATION (See Listing 1, a GUI-independent header file for the
  20599. library). The application will create an object of this class; this object
  20600. should be created first, and destroyed last in any application.
  20601. The main entities an application creates are windows. These are implemented in
  20602. the class GUI_WINDOW. A window in this context has a frame, with some possible
  20603. decorations (title, maximize button, sizable-border, etc.), and usually has
  20604. one or more children. This library implements the child types static text,
  20605. button, entry field, line, and ellipse. The corresponding classes are
  20606. GUI_TEXT, GUI_BUTTON, GUI_ENTRY, GUI_LINE, and GUI_ELLIPSE.
  20607. These classes naturally fall into two groups: those implemented in the native
  20608. toolkit as special native windows, and those that must be created by some
  20609. drawing APIs. The two groups descend from two different base classes, since
  20610. they behave differently in many respects. (For instance, most of the native
  20611. windows are repainted automatically as needed, the other type are not.)
  20612. The two base classes are GUI_WINDOW_OBJECT and GUI_GRAPHICS_OBJECT.
  20613. GUI_WINDOW_OBJECT is the common base class for GUI_TEXT, GUI_BUTTON,
  20614. GUI_ENTRY, and GUI_WINDOW, whose objects are native windows.
  20615. GUI_GRAPHICS_OBJECT is the common base class for GUI_LINE and GUI_ELLIPSE,
  20616. whose objects must be drawn explicitly by the application.
  20617. Though they behave differently, these two base classes share some common
  20618. elements. For instance, all of them have x and y coordinates, they have width
  20619. and height, etc. To extract these and some other common characteristics, I
  20620. introduce a common base class, GUI_OBJECT. Figure 1 shows the class heirarchy
  20621. just described.
  20622.  
  20623.  
  20624. Processing User Input
  20625.  
  20626.  
  20627. Most of the objects mentioned above must be able to accept user input and
  20628. execute some functions based upon it. For instance, the button objects must
  20629. accept a LEFT MOUSE BUTTON SINGLE CLICK event and execute an
  20630. application-defined function. There are two ways to accomplish this.
  20631. The first way is to store event-handler function pointers in the
  20632. above-mentioned objects. The application will call the desired function
  20633. through one of these pointers when a given event takes place. The other way to
  20634. provide event responses is to create some virtual functions in the base class,
  20635. to be overridden by an application-derived class. I chose the second approach,
  20636. since it seems more modular and cleaner to me.
  20637. I provide a default LeftButtonSingleClick function for every class, which
  20638. function does nothing. Thus, a mouse click on a button of type GUI_BUTTON
  20639. causes no harm, but not much excitement either. If you derive a class
  20640. MY_BUTTON from GUI_BUTTON, and override LeftButtonSingleClick, then this new
  20641. overridden function will be called whenever a MY_BUTTON is clicked. I do not
  20642. use C++'s virtual function call mechanism to determine which button was
  20643. pushed. To do so would require a new derived button class for practically
  20644. every button in the program. Rather, I find out which button was activated by
  20645. introducing a numeric ID field for every GUI_OBJECT, enabling a switch
  20646. statement to be based upon this ID.
  20647. These user input functions could be implemented in the GUI_OBJECT class, but I
  20648. decided to implement them in a base class for GUI_OBJECT, CALLABLE_OBJECT. (If
  20649. my class hierarchy were to grow, a new object class may not necessarily be
  20650. derived from GUI_OBJECT, but I still might want to be able to process user
  20651. input for that class.)
  20652.  
  20653.  
  20654. Display Features
  20655.  
  20656.  
  20657. My library assumes a window-relative MAX_X by MAX_Y coordinate system, with
  20658. the upper left corner as (0,0). In such a coordinate system, a window with a
  20659. width of MAX_X spans the whole width of the screen, while a button with a
  20660. width of MAX_X spans the whole width of its parent window. The particular
  20661. values for MAX_X and for MAX_Y are not really important but they should be
  20662. finer than the actual display resolution. This library uses 10,000 for both
  20663. MAX_X and MAX_Y, which is a safe choice for most contemporary displays.
  20664. Matching appropriate colors among the different GUI environment is a subject
  20665. worth a book in itself. Therefore, I chose a very simple approach and
  20666. implemented the 16 VGA colors. Font handling is another issue that naturally
  20667. arises, but its complexity is beyond the scope of this article. (However, a
  20668. subset of available fonts could be easily managed in all of the mentioned
  20669. environments.)
  20670.  
  20671.  
  20672. Description of Classes
  20673.  
  20674.  
  20675. In this section I describe the platform-independent portion of the class
  20676. library. Figure 1 shows the class hierarchy. The top three layers of the
  20677. hierarchy consist of abstract classes. The CALLABLE_OBJECT class declares
  20678. several functions for responding to user input, but these are all virtual
  20679. functions, hence they must be overridden and "filled in" in descendant
  20680. classes. The next lower class, GUI_OBJECT, introduces a data member id, which
  20681. stores an ID number for each object. Every GUI_OBJECT must have a unique ID. I
  20682. also briefly describe class GUI_APPLICATION here, though it is not a member of
  20683. the hierarchy.
  20684.  
  20685.  
  20686. GUI_APPLICATION
  20687.  
  20688.  
  20689. The GUI_APPLICATION class has no descendants. It serves as a wrapper around
  20690. the application and maintains the main message loop. A GUI_APPLICATION object
  20691. has four basic functions: a constructor to initialize the GUI toolkit, a
  20692. destructor to clean up, a MainLoop function to handle the main message loop,
  20693. and a Terminate function which breaks the message loop. This class maintains a
  20694. single-linked list of GUI_WINDOW objects, the windows of the application.
  20695.  
  20696.  
  20697. GUI_WINDOW_OBJECT
  20698.  
  20699.  
  20700. GUI_WINDOW_OBJECT serves as an abstract base class for GUI_WINDOW. A
  20701. GUI_WINDOW object maintains a handle to its own native window, in protected
  20702. member window of GUI_WINDOW_OBJECT. Each GUI_WINDOW object also maintains a
  20703. single-linked list of GUI_OBJECT objects comprising its children, such as
  20704. buttons, text elements, lines, etc. (Note that these "children" are not
  20705. descendants of GUI_WINDOW; rather, they are children in the sense that they
  20706. are owned by a GUI_WINDOW object.) When a GUI_WINDOW object is created it does
  20707. not appear immediately, therefore the application has a chance to create its
  20708. children first. Calling GUI_WINDOW's Show function finally makes it appear.
  20709. GUI_WINDOW also has a function Message to put up a message box, and a function
  20710. Question to ask the user a yes/no question. GUI_WINDOW's GetText/SetText
  20711. function pair enables an application to dynamically get/set the title of its
  20712. window.
  20713. GUI_TEXT, GUI_BUTTON, and GUI_ENTRY are descendants of GUI_WINDOW_OBJECT whose
  20714. objects function as children of GUI_WINDOW objects. A GUI_TEXT object contains
  20715. a non-modifiable piece of text to be displayed in the window. The user cannot
  20716. change its contents. A GUI_BUTTON object represents a pushbutton. A GUI_ENTRY
  20717. object represents a single-line, specified-text-length, autoscrolling entry
  20718. field. These are the only two classes in this library that override the
  20719. user-input functions declared in CALLABLE_OBJECT.
  20720. Specifically, a GUI_BUTTON object calls the following functions:
  20721. Activate. The button object calls this function if the user activates the
  20722. button, either through the mouse or through the keyboard
  20723. LeftClick. Called when the GUI_BUTTON is clicked with the left mouse button
  20724. LeftDoubleClick. Called when the GUI_BUTTON is double-clicked with the left
  20725. mouse button
  20726. RightClick. Called when the GUI_BUTTON is clicked with the right mouse button
  20727. RightDoubleClick. Called when the GUI_BUTTON is double-clicked with the right
  20728. mouse button
  20729. A GUI_ENTRY object calls the Activate function if the entry field is losing
  20730. its focus.
  20731. Supplying a height parameter of zero to a GUI_TEXT, GUI_BUTTON, or GUI_ENTRY
  20732. object will cause it to be created with the default font height. It is also
  20733. possible to dynamically get/set these objects' text via the GetText/SetText
  20734. function pair.
  20735.  
  20736.  
  20737. GUI_GRAPHICS_OBJECT
  20738.  
  20739.  
  20740. A GUI_GRAPHICS_OBJECT is an abstract base class for objects that can't be
  20741. represented as native windows. It has two descendants, GUI_ELLIPSE and
  20742. GUI_LINE. A GUI_ELLIPSE object represents a filled/outlined color-specified
  20743. ellipse. A GUI_LINE object represents a color-specified straight line.
  20744.  
  20745.  
  20746. Class Implementations
  20747.  
  20748.  
  20749.  
  20750. I've divided the source for this class library (about 750 lines on every
  20751. platform) into two parts: gui.c (Listing 2) contains functions that don't use
  20752. native APIs; native.c, provided separately for each platform, contains
  20753. functions that do use native APIs. Due to space limitations, the three
  20754. native.c files are not shown here, but are available on this month's code disk
  20755. and CUJ online sources (see p. 3 for more information on online sources).
  20756. At a structural level, each native implementation shares some common features.
  20757. For example, each implementation must perform some sort of registration of a
  20758. window or application with the operating system. Each implementation must
  20759. interact with a main message loop and respond to messages from the system.
  20760. Finally, each implementation must explicitly draw graphic objects of type
  20761. GUI_LINE and GUI_ELLIPSE, rather than rely upon the native system to do the
  20762. work.
  20763. The greatest difference in implementations occurs between the Windows
  20764. look-alikes (Windows 3.1, Windows NT, and OS/2) and X-Window. For example,
  20765. X-Window requires an application to register callback functions for painting
  20766. and resizing windows, while the look-alikes do this in response to system
  20767. messages (e.g. WM_PAINT). Another significant difference appears in the way
  20768. the two implementations manage to catch all mouse events for nongraphical
  20769. windows. The Windows look-alikes do so by "subclassing the window," overriding
  20770. the built-in window procedure for buttons. The X implementation catches such
  20771. events by registering an event handler with the system.
  20772. More details, as well as documentation, are provided on this month's code
  20773. disk.
  20774.  
  20775.  
  20776. A Short Demo
  20777.  
  20778.  
  20779. The application presented here (Listing 3), while not extremely "useful,"
  20780. demonstrates how easy it is to build an application using the library. Figure
  20781. 2 is a screen capture of the demo running on AIX/X-Windows. The application
  20782. window's Exit button terminates the application, while its Next button creates
  20783. an identical window, shifted a little to the upper left and displayed in a
  20784. different color. The application window's entry field determines the title of
  20785. the window. Both of the window's buttons react to right-mouse-button clicks
  20786. with popup messages.
  20787.  
  20788.  
  20789. Conclusion
  20790.  
  20791.  
  20792. Constructing a multi-platform GUI is not a simple task. Many problems will
  20793. naturally arise that this article does not address: drawing optimization;
  20794. creation of menu bars, accelerator keys, and popup menus; implementation of
  20795. drag and drop operations, and z-order for children of a window; font
  20796. management and national language support, just to mention a few. Some of these
  20797. features can be added with relative ease (menus, optimized drawing, z-order),
  20798. while others may require considerable effort (font management, drag and drop).
  20799. However, if you're the kind of programmer who likes to experiment, or "roll
  20800. your own," this class library should give you a good place to start.
  20801.  
  20802.  
  20803. Information Sources
  20804.  
  20805.  
  20806. Borland C++ for 0S/2 Ver 1.5, on CD-ROM. Produced by Borland International,
  20807. 1994.
  20808. Microsoft Development Platform, on CD-ROM. Produced by Microsoft Corp., 1995.
  20809. O'Reilly, Tim, ed. X series, Release 4 and Release 5, vols. 0 - 8. O'Really &
  20810. Associates, 1990-1994.
  20811. Figure 1 The Library Class Hierarchy
  20812. Figure 2 Demo on AIX/X-Window
  20813.  
  20814. Listing 1 gui.h -- Platform-independent header file for class library
  20815. gui.h
  20816.  
  20817. #ifndef _GUI_____LINEEND____
  20818. #define _GUI_____LINEEND____
  20819.  
  20820. #include <stdarg.h>
  20821.  
  20822. // platform specific includes and defines
  20823.  
  20824. #ifdef WIN_NT
  20825. #include <windows.h>
  20826.  
  20827. typedef HWND NativeWindow;
  20828. typedef HDC GraphicsHandle;
  20829. #define EXP1
  20830. #define EXP2 _declspec(dllexport)
  20831. #define NATIVE_PART_OF_GUI_APPLICATION HINSTANCE hInstance
  20832. #define NATIVE_PART_OF_GUI_WINDOW
  20833. #define NATIVE_FRIEND_OF_GUI_WINDOW friend LRESULT CALLBACK \
  20834. GuiWindowProc(HWND hWnd, UINT message, \
  20835. WPARAM uParam, LPARAM lParam)
  20836. #define main ApplicationMain
  20837. #endif
  20838.  
  20839. #ifdef OS2
  20840. #define INCL_WIN
  20841. #define INCL_GPI
  20842. #include <os2.h>
  20843.  
  20844. typedef HWND NativeWindow;
  20845.  
  20846. typedef HPS GraphicsHandle;
  20847. #define EXP1 _export
  20848. #define EXP2
  20849. #define NATIVE_PART_OF_GUI_APPLICATION HAB Hab; \
  20850. HMQ Hmq
  20851. #define NATIVE_PART_OF_GUI_WINDOW NativeWindow frame
  20852. #define NATIVE_FRIEND_OF_GUI_WINDOW friend MRESULT \
  20853. EXPENTRY GuiWindowProc(HWND hwnd, USHORT msg, \
  20854. MPARAM mp1, MPARAM mp2)
  20855. #endif
  20856.  
  20857. #ifdef X_WINDOWS
  20858. #include <X11/Intrinsic.h>
  20859.  
  20860. typedef Widget NativeWindow;
  20861. typedef GC GraphicsHandle;
  20862. #define EXP1
  20863. #define EXP2
  20864. #define NATIVE_PART_OF_GUI_APPLICATION int dontQuit
  20865. #define NATIVE_PART_OF_GUI_WINDOW NativeWindow frame; \
  20866. char szGeometry[100]
  20867. #define NATIVE_FRIEND_OF_GUI_WINDOW \
  20868. friend void ExposeCallback(Widget w, \
  20869. XtPointer client_data, \
  20870. XtPointer call_data); \
  20871. friend void ResizeCallback(Widget w, \
  20872. XtPointer client_data, \
  20873. XtPointer call_data)
  20874. #endif
  20875.  
  20876. // coordinate system definition
  20877.  
  20878. #define MAX_X 10000
  20879. #define MAX_Y 10000
  20880.  
  20881. #define MAX_WINDOW 100 // max. windows in an application
  20882. #define MAX_CHILDREN 100 // max. children of a window
  20883.  
  20884. // VGA colors
  20885.  
  20886. #define COLOR_WHITE 0
  20887. #define COLOR_BLACK 1
  20888. #define COLOR_BLUE 2
  20889. #define COLOR_RED 3
  20890. #define COLOR_PINK 4
  20891. #define COLOR_GREEN 5
  20892. #define COLOR_CYAN 6
  20893. #define COLOR_YELLOW 7
  20894. #define COLOR_NEUTRAL 8
  20895. #define COLOR_DARKGRAY 9
  20896. #define COLOR_DARKBLUE 10
  20897. #define COLOR_DARKRED 11
  20898. #define COLOR_DARKPINK 12
  20899. #define COL0R_DARKGREEN 13
  20900. #define COLOR_DARKCYAN 14
  20901. #define COLOR_BROWN 15
  20902.  
  20903. // object types returned by GetType()
  20904.  
  20905.  
  20906. #define TYPE_LINE 1
  20907. #define TYPE_ELLIPSE 2
  20908. #define TYPE_TEXT 3
  20909. #define TYPE_BUTTON 4
  20910. #define TYPE_ENTRY 5
  20911. #define TYPE_WINDOW 6
  20912.  
  20913. // the only global function: logs into a file
  20914. EXP2 void EXP1 Log(char* fmt, ...);
  20915.  
  20916. // forward declaration of classes GUI_WINDOW and GUI_OBJECT
  20917. class GUI_WINDOW;
  20918. class GUI_OBJECT;
  20919.  
  20920. typedef struct _GUI_WINDOW_ELEM { // typedef for linked list of windows
  20921. GUI_WINDOW* pWindow;
  20922. struct _GUI_WINDOW_ELEM* pNext;
  20923. } GUI_WINDOW_ELEM;
  20924.  
  20925. class EXP1 GUI_APPLICATION {
  20926. private:
  20927. NATIVE_PART_OF_GUI_APPLICATION;
  20928.  
  20929. GUI_WINDOW_ELEM WindowElemList[MAX_WINDOW], *pUsed, *pFree;
  20930. int AddWindow(GUI_WINDOW *pWindow);
  20931. int DelWindow(GUI_WINDOW *pWindow);
  20932. public:
  20933. EXP2 GUI_APPLICATION(int* pArgc, char** argv, char* logFile);
  20934. EXP2 virtual ~GUI_APPLICATION(void);
  20935. EXP2 void MainLoop(void);
  20936. EXP2 void Terminate(void);
  20937. EXP2 GUI_WINDOW* FindWindow(NativeWindow window);
  20938. EXP2 GUI_OBJECT* FindChild(NativeWindow window);
  20939.  
  20940. friend class GUI_WINDOW;
  20941. };
  20942.  
  20943. class EXP1 CALLABLE_OBJECT {
  20944. public:
  20945. EXP2 virtual int Activate(void) { return(1); }
  20946. EXP2 virtual int LeftClick(void) { return(1); }
  20947. EXP2 virtual int RightClick(void) { return(1); }
  20948. EXP2 virtual int LeftDoubleClick(void) { return(1); }
  20949. EXP2 virtual int RightDoubleClick(void) { return(1); }
  20950. };
  20951.  
  20952. class EXP1 GUI_OBJECT: public CALLABLE_OBJECT { // abstract class
  20953. protected:
  20954. GUI_WINDOW* pParent;
  20955. int id, x, y, width, height;
  20956. public:
  20957. EXP2 GUI_OBJECT(GUI_WINDOW* pParent, int id, int x=0, int y=0,
  20958. int width=MAX_X, int height=MAX_Y);
  20959. EXP2 virtual ~GUI_OBJECT(void);
  20960. EXP2 virtual int GetType(void)=0;
  20961. EXP2 virtual void Paint(GraphicsHandle gh) { }
  20962. EXP2 virtual NativeWindow GetNativeWindow(void) { return(0); }
  20963. EXP2 GUI_WINDOW* GetParent(void) { return(pParent); }
  20964. EXP2 int GetId(void) { return(id); }
  20965.  
  20966. EXP2 int GetX(void) { return(x); }
  20967. EXP2 int GetY(void) { return(y); }
  20968. EXP2 int GetWidth(void) { return(width); }
  20969. EXP2 int GetHeight(void) { return(height); }
  20970. };
  20971.  
  20972. class EXP1 GUI_GRAPHICS_OBJECT: public GUI_OBJECT { // abstract class
  20973. protected:
  20974. int color, lineWidth;
  20975. public:
  20976. EXP2 GUI_GRAPHICS_OBJECT(GUI_WINDOW* pParent, int id, int x=0,
  20977. int y=0,int width=MAX_X, int height=MAX_Y,
  20978. int color=COLOR_BLACK, int lineWidth=1);
  20979. EXP2 virtual ~GUI_GRAPHICS_OBJECT(void);
  20980. };
  20981.  
  20982. class EXP1 GUI_LINE: public GUI_GRAPHICS_OBJECT {
  20983. public:
  20984. EXP2 GUI_LINE(GUI_WINDOW* pParent, int id, int x1=0, int y1=0,
  20985. int x2=MAX_X, int y2=MAX_Y,
  20986. int color=COLOR_BLACK, int lineWidth=1);
  20987. EXP2 virtual ~GUI_LINE(void);
  20988. EXP2 virtual int GetType(void) { return(TYPE_LINE); }
  20989. EXP2 virtual void Paint(GraphicsHandle gh);
  20990. };
  20991.  
  20992. // fill types
  20993.  
  20994. #define FILL_OUTER 0 // draw just the otline
  20995. #define FILL_SOLID 1 // draw and fill the shape
  20996.  
  20997. class EXP1 GUI_ELLIPSE: public GUI_GRAPHICS_OBJECT { // abstract class
  20998. protected:
  20999. int fillType;
  21000. public:
  21001. EXP2 GUI_ELLIPSE(GUI_WINDOW* pParent, int id, int x=0, int y=0,
  21002. int width=MAX_X/2, int height=MAX_Y/2,
  21003. int color=COLOR_BLACK, int fillType=FILL_OUTER,
  21004. int lineWidth=1);
  21005. EXP2 virtual ~GUI_ELLIPSE(void);
  21006. EXP2 virtual int GetType(void) { return(TYPE_ELLIPSE); }
  21007. EXP2 virtual void Paint(GraphicsHandle gh);
  21008. };
  21009.  
  21010. class EXP1 GUI_WINDOW_OBJECT: public GUI_OBJECT { // abstract class
  21011. protected:
  21012. NativeWindow window;
  21013. public:
  21014. EXP2 GUI_WINDOW_OBJECT(GUI_WINDOW* pParent, int id, int x=0,
  21015. int y=0, int width=MAX_X, int height=MAX_Y);
  21016. EXP2 virtual ~GUI_WINDOW_OBJECT(void);
  21017. EXP2 NativeWindow GetNativeWindow(void) { return(window); }
  21018. EXP2 virtual void GetRealCoordinates(int *pRx, int *pRy,
  21019. int *pRw, int *pRh);
  21020. EXP2 virtual int GetText(char *buffer, int bufferLength);
  21021. EXP2 virtual int SetText(char *text="");
  21022. };
  21023.  
  21024. class EXP1 GUI_TEXT: public GUI_WINDOW_OBJECT {
  21025.  
  21026. public:
  21027. EXP2 GUI_TEXT(GUI_WINDOW* pParent, int id, int x=0, int y=0,
  21028. int width=MAX_X, int height=0,
  21029. char *text="");
  21030. EXP2 virtual int GetType(void) { return(TYPE_TEXT); }
  21031. };
  21032.  
  21033. class EXP1 GUI_BUTTON: public GUI_WINDOW_OBJECT {
  21034. public:
  21035. EXP2 GUI_BUTTON(GUI_WINDOW* pParent, int id, int x=0, int y=0,
  21036. int width=MAX_X, int height=0,
  21037. char* text="");
  21038. EXP2 virtual int GetType(void) { return(TYPE_BUTTON; }
  21039. };
  21040.  
  21041. class EXP1 GUI_ENTRY: public GUI_WINDOW_OBJECT {
  21042. protected:
  21043. int length;
  21044. public:
  21045. EXP2 GUI_ENTRY(GUI_WINDOW* pParent, int id, int x=0, int y=0,
  21046. int width=MAX_X, int height=0,
  21047. char* text="", int maxTextLength=10);
  21048. EXP2 virtual int GetType(void) { return(TYPE_ENTRY); }
  21049. #ifdef X_WINDOWS // if not under X_WINDOWS these are inherited from
  21050. // GUI_WINDOW_OBJECT
  21051. EXP2 virtual int GetText(char *buffer, int bufferLength);
  21052. EXP2 virtual int SetText(char *text="");
  21053. #endif
  21054. };
  21055.  
  21056. // frame styles
  21057.  
  21058. #define FRAME_THIN 1
  21059. #define FRAME_SIZEABLE 2
  21060. #define FRAME_TITLE 4
  21061. #define FRAME_MIN_BUTTON 8
  21062. #define FRAME_MAX_BUTTON 16
  21063. #define FRAME_DEFAULT (FRAME_THIN FRAME_TITLE)
  21064.  
  21065. typedef struct _GUI_OBJECT_ELEM { // typedef for linked list of children
  21066. GUI_OBJECT* pObject;
  21067. struct _GUI_OBJECT_ELEM* pNext;
  21068. } GUI_OBJECT_ELEM;
  21069.  
  21070. class EXP1 GUI_WINDOW: public GUI_WINDOW_OBJECT {
  21071. private:
  21072. NATIVE_PART_OF_GUI_WINDOW;
  21073.  
  21074. GUI_APPLICATION* pApplication;
  21075. GraphicsHandle gh;
  21076. int frameStyle, backgroundColor;
  21077. GUI_OBJECT_ELEM Children[MAX_CHILDREN], *pUsed, *pFree;
  21078. protected:
  21079. EXP2 void SizeChildren(void);
  21080. EXP2 int AddChild(GUI_OBJECT* pChild);
  21081. EXP2 int DeleteChild(GUI_OBJECT* pChild);
  21082. public:
  21083. EXP2 GUI_WINDOW(GUI_APPLICATION* pApplication, int id, int x=0,
  21084. int y=0, int width=MAX_X, int height=MAX_Y,
  21085.  
  21086. int backgroundColor=COLOR_WHITE, char* title= "",
  21087. int frameStyle=FRAME_DEFAULT);
  21088. EXP2 virtual ~GUI_WINDOW(void);
  21089. EXP2 virtual int GetType(void) { return(TYPE_WINDOW); }
  21090. EXP2 virtual void Paint(GraphicsHandle gh);
  21091. EXP2 virtual void Show(void);
  21092. EXP2 int GetBackground(void) {return(backgroundColor); }
  21093. EXP2 GraphicsHandle GetGraphicsHandle(void) { return(gh); }
  21094. EXP2 GUI_OBJECT* GetChildFromId(int id);
  21095. EXP2 GUI_OBJECT* GetChildFromWindow(NativeWindow window);
  21096. #ifndef WIN_NT // if under Windows these are inherited from GUI_WINDOW_OBJECT
  21097. EXP2 virtual int GetText(char *buffer, int bufferLength);
  21098. EXP2 virtual int SetText(char *text="");
  21099. #endif
  21100. EXP2 virtual void Message(char *title, char *text);
  21101. // return value 0=OK, 1=Cancel
  21102. EXP2 virtual int Question(char *title, char *text);
  21103.  
  21104. friend class GUI_APPLICATION;
  21105. friend class GUI_OBJECT;
  21106. NATIVE_FRIEND_OF_GUI_WINDOW;
  21107. };
  21108.  
  21109. #endif // #ifndef _GUI_____LINEEND____
  21110.  
  21111. /* End of File */
  21112.  
  21113.  
  21114. Listing 2 gui.c -- Implementation of platform-independent functions
  21115. #include <stdio.h>
  21116. #include <stdlib.h>
  21117. #include <string.h>
  21118.  
  21119. #include "gui.h"
  21120.  
  21121. extern int charHeight;
  21122. extern FILE* fErrLog;
  21123. extern GUI_APPLICATION* pGuiApplication;
  21124.  
  21125. //*** logging function into a file opened by GUI_APPLICATION
  21126.  
  21127. void Log(char* fmt, ...)
  21128. {
  21129. va_list ap;
  21130.  
  21131. if(fErrLog) {
  21132. va_start(ap, fmt);
  21133. vfprintf(fErrLog, fmt, ap);
  21134. fflush(fErrLog); // to protect against losing log if crashing
  21135. va_end(ap);
  21136. }
  21137. }
  21138.  
  21139. //*** Functions for GUI_APPLICATION
  21140.  
  21141. int GUI_APPLICATION::AddWindow(GUI_WINDOW *pWindow)
  21142. {
  21143. int ret = 1;
  21144. GUI_WINDOW_ELEM* pTmp;
  21145.  
  21146.  
  21147. // add a GUI_WINDOW to the linked list of windows
  21148. if(pFree) {
  21149. pTmp = pFree;
  21150. pFree = pFree->pNext;
  21151. pTmp->pWindow = pWindow;
  21152. pTmp->pNext = pUsed;
  21153. pUsed = pTmp;
  21154. ret = 0;
  21155. }
  21156.  
  21157. if(ret) Log("Error: too many windows.\n");
  21158.  
  21159. return(ret);
  21160. }
  21161.  
  21162. int GUI_APPLICATION::DelWindow(GUI_WINDOW* pWindow)
  21163. {
  21164. int ret = 1;
  21165. GUI_WINDOW_ELEM *pTmp, *pPrev;
  21166.  
  21167. // delete a window from the linked lists of windows
  21168. for(pTmp = pUsed, pPrev = NULL; pTmp; pPrev = pTmp,
  21169. pTmp = pTmp->pNext) {
  21170. if(pTmp->pWindow == pWindow) {
  21171. // take out of used list
  21172. if(pPrev) pPrev->pNext = pTmp->pNext;
  21173. else pUsed = pTmp->pNext;
  21174. // put into free list
  21175. pTmp->pNext = pFree;
  21176. pFree = pTmp;
  21177. ret = 0;
  21178. break;
  21179. }
  21180. }
  21181.  
  21182. if(ret) Log("Error: can't delete window %p\n", pWindow);
  21183.  
  21184. return(ret);
  21185. }
  21186.  
  21187. GUI_WINDOW* GUI_APPLICATION::FindWindow(NativeWindow window)
  21188. {
  21189. GUI_WINDOW_ELEM *pTmp;
  21190.  
  21191. // find a window in its list of windows
  21192. for(pTmp = pUsed; pTmp; pTmp = pTmp->pNext) {
  21193. if(pTmp->pWindow->GetNativeWindow() == window) break;
  21194. }
  21195.  
  21196. return(pTmp ? pTmp->pWindow : NULL);
  21197. }
  21198.  
  21199. //*** Functions for GUI_OBJECT
  21200.  
  21201. GUI_OBJECT::GUI_OBJECT(GUI_WINDOW* pParent, int id, int x, int y,
  21202. int width, int height)
  21203. : id(id), pParent(pParent), x(x), y(y),
  21204. width(width), height(height)
  21205.  
  21206. {
  21207. // add this object to its parent children list
  21208. if(pParent) pParent->AddChild(this);
  21209. }
  21210.  
  21211. GUI_OBJECT::~GUI_OBJECT(void)
  21212. {
  21213. // delete this object from its parent children list
  21214. if(pParent) pParent->DeleteChild(this);
  21215. }
  21216.  
  21217. //*** Functions for GUI_GRAPHICS_OBJECT
  21218.  
  21219. GUI_GRAPHICS_OBJECT::GUI_GRAPHICS_OBJECT(GUI_WINDOW* pParent, int id,
  21220. int x, int y,
  21221. int width, int height,
  21222. int color, int lineWidth)
  21223. :GUI_OBJECT(pParent, id, x, y, width, height),
  21224. color(color), lineWidth(lineWidth)
  21225. {
  21226. // nothing to do
  21227. }
  21228.  
  21229. GUI_GRAPHICS_OBJECT::~GUI_GRAPHICS_OBJECT(void)
  21230. {
  21231. // nothing to do
  21232. }
  21233.  
  21234. //*** Functions for GUI_LINE
  21235.  
  21236. GUI_LINE::GUI_LINE(GUI_WINDOW* pParent, int id, int x1, int y1, int x2,
  21237. int y2, int color, int lineWidth)
  21238. :GUI_GRAPHICS_OBJECT(pParent, id, x1, y1, x2, y2, color, lineWidth)
  21239. {
  21240. // paint the first time around
  21241. Paint(pParent->GetGraphicsHandle());
  21242. }
  21243.  
  21244. //*** Functions for GUI_ELLIPSE
  21245.  
  21246. GUI_ELLIPSE::GUI_ELLIPSE(GUI_WINDOW* pParent, int id, int x, int y,
  21247. int width, int height,
  21248. int color, int fillType, int lineWidth)
  21249. :GUI_GRAPHICS_OBJECT(pParent, id, x, y, width, height,
  21250. color, lineWidth),
  21251. fillType(fillType)
  21252. {
  21253. // paint the first time around
  21254. Paint(pParent->GetGraphicsHandle());
  21255. }
  21256.  
  21257. //*** Functions for GUI_WINDOW_OBJECT
  21258.  
  21259. GUI_WINDOW_OBJECT::GUI_WINDOW_OBJECT(GUI_WINDOW* pParent, int id,
  21260. int x, int y, int width, int height)
  21261. :GUI_OBJECT(pParent, id, x, y, width, height)
  21262. {
  21263. window = 0;
  21264. }
  21265.  
  21266.  
  21267. //*** Functions for GUI_WINDOW
  21268.  
  21269. int GUI_WINDOW::AddChild(GUI_OBJECT* pChild)
  21270. {
  21271. int ret = 1;
  21272. GUI_OBJECT_ELEM* pTmp;
  21273.  
  21274. // add pChild to the linked list of children
  21275. if(pFree) {
  21276. pTmp = pFree;
  21277. pFree = pFree->pNext;
  21278. pTmp->pObject = pChild;
  21279. pTmp->pNext = pUsed;
  21280. pUsed = pTmp;
  21281. ret = 0;
  21282. }
  21283.  
  21284. if(ret) Log("Error: too many children.\n");
  21285.  
  21286. return(ret);
  21287. }
  21288.  
  21289. int GUI_WINDOW::DeleteChild(GUI_OBJECT* pChild)
  21290. {
  21291. int ret = 1;
  21292. GUI_OBJECT_ELEM *pTmp, *pPrev;
  21293.  
  21294. // delete pChild from the linked list of children
  21295. for(pTmp = pUsed, pPrev = NULL; pTmp; pPrev = pTmp,
  21296. pTmp = pTmp->pNext) {
  21297. if(pTmp->pObject == pChild) {
  21298. // take out of used list
  21299. if(pPrev) pPrev->pNext = pTmp->pNext;
  21300. else pUsed = pTmp->pNext;
  21301. // put into free list
  21302. pTmp->pNext = pFree;
  21303. pFree = pTmp;
  21304. ret = 0;
  21305. break;
  21306. }
  21307. }
  21308.  
  21309. if(ret) Log("Error: can't delete child %p\n", pChild);
  21310.  
  21311. return(ret);
  21312. // get child pointer from its id; it returns NULL if fails
  21313.  
  21314. GUI_OBJECT* GUI_WINDOW::GetChildFromId(int id)
  21315. {
  21316. GUI_OBJECT_ELEM *pTmp;
  21317.  
  21318. for(pTmp = pUsed; pTmp; pTmp = pTmp->pNext) {
  21319. if(pTmp->pObject->GetId() == id) break;
  21320. }
  21321.  
  21322. return(pTmp ? pTmp->pObject : NULL);
  21323. }
  21324.  
  21325.  
  21326. // get child pointer from its native window; it returns NULL if fails
  21327.  
  21328. GUI_OBJECT* GUI_WINDOW::GetChildFromWindow(NativeWindow hwnd)
  21329. {
  21330. GUI_OBJECT_ELEM *pTmp;
  21331.  
  21332. for(pTmp = pUsed; pTmp; pTmp = pTmp->pNext) {
  21333. if(pTmp->pObject->GetNativeWindow() == hwnd) break;
  21334. }
  21335.  
  21336. return(pTmp ? pTmp->pObject : NULL);
  21337. }
  21338.  
  21339. /* End of File */
  21340.  
  21341.  
  21342. Listing 3 demo.c -- Short demonstration program
  21343. #include <stdio.h>
  21344. #include "gui.h"
  21345.  
  21346. #define MY_EXIT 1
  21347. #define MY_NEW 2
  21348. #define MY_CLOSE 3
  21349. #define MY_TEXT 4
  21350. #define MY_NAME 5
  21351.  
  21352. class MY_BUTTON: public GUI_BUTTON {
  21353. public:
  21354. MY_BUTTON(GUI_WINDOW *pParent, int id, int x=0, int y=0,
  21355. int width=MAX_X, int height=MAX_Y, char *text="")
  21356. :GUI_BUTTON(pParent, id, x, y, width, height, text) {}
  21357. int Activate(void);
  21358. int RightClick(void);
  21359. };
  21360.  
  21361. class MY_ENTRY: public GUI_ENTRY {
  21362. public:
  21363. MY_ENTRY(GUI_WINDOW* pParent, int id, int x=0, int y=0,
  21364. int width=MAX_X, int height=0,
  21365. char* text="", int length=10)
  21366. :GUI_ENTRY(pParent, id, x, y, width, height, text,
  21367. length) {}
  21368. int Activate(void);
  21369. };
  21370.  
  21371. GUI_APPLICATION *pApplication;
  21372.  
  21373. void CreateMyWindow(int id);
  21374. void DeleteMyWindow(GUI_WINDOW *pWindow);
  21375.  
  21376. void main(int argc, char **argv)
  21377. {
  21378. pApplication = new GUI_APPLICATION(&argc, argv, "demo.log");
  21379. CreateMyWindow(1);
  21380. pApplication->MainLoop();
  21381. delete pApplication;
  21382. }
  21383.  
  21384. int MY_BUTTON::Activate(void)
  21385.  
  21386. {
  21387. static int windowCount = 1;
  21388.  
  21389. switch(GetId() - pParent->GetId()) {
  21390. case MY_EXIT:
  21391. pApplication->Terminate();
  21392. break;
  21393. case MY_NEW:
  21394. if(windowCount < 10) {
  21395. CreateMyWindow(pParent->GetId() + 100);
  21396. windowCount++;
  21397. }
  21398. else {
  21399. GetParent()->Message("Application message",
  21400. "Too many windows");
  21401. }
  21402. break;
  21403. case MY_CLOSE:
  21404. if(--windowCount) DeleteMyWindow(GetParent());
  21405. else pApplication->Terminate();
  21406. break;
  21407. }
  21408. return(0);
  21409. }
  21410.  
  21411. int MY_BUTTON::RightClick(void)
  21412. {
  21413. GetParent()->Message("Applicaton Message", "RightClick");
  21414. return(0);
  21415. }
  21416.  
  21417. int MY_ENTRY::Activate(void)
  21418. {
  21419. char text[20+1];
  21420.  
  21421. GetText(text, 20);
  21422. GetParent()->SetText(text);
  21423. return(0);
  21424. }
  21425.  
  21426. void CreateMyWindow(int id)
  21427. {
  21428. int color, childId;
  21429. char title[100];
  21430. GUI_WINDOW *pWindow;
  21431.  
  21432. color = id / 100;
  21433. sprintf(title, "Window %d", id);
  21434. pWindow = new GUI_WINDOW(pApplication, id, 15*id, 4900 - 10*id,
  21435. 5000, 5000, color, title,
  21436. FRAME_TITLE FRAME_SIZEABLE FRAME_MAX_BUTTON);
  21437.  
  21438. // create buttons, text and entry field
  21439. new MY_BUTTON(pWindow, id+MY_EXIT, 1000, 9000, 2000, 0, "Exit");
  21440. new MY_BUTTON(pWindow, id+MY_CLOSE, 4000, 9000, 2000, 0, "Close");
  21441. new MY_BUTTON(pWindow, id+MY_NEW, 7000, 9000, 2000, 0, "New");
  21442. new GUI_TEXT(pWindow, id+MY_TEXT, 1000, 7500, 2500, 0, "New title:");
  21443. new MY_ENTRY(pWindow, id+MY_MAME, 4000, 7500, 2000, 0, "", 20);
  21444.  
  21445.  
  21446. // create little figure
  21447. childId = id + MY_NAME;
  21448. //body
  21449. new GUI_LINE(pWindow, ++childId, 7500, 7000, 8000, 6500, color+1, 2);
  21450. new GUI_LINE(pWindow, ++childId, 8500, 7000, 8000, 6500, color+1, 2);
  21451. new GUI_LINE(pWindow, ++childId, 8000, 6500, 8000, 4000, color+1, 2);
  21452. new GUI_LINE(pWindow, ++childId, 7500, 5000, 8000, 4500, color+1, 2);
  21453. new GUI_LINE(pWindow, ++childId, 8500, 5000, 8000, 4500, color+1, 2);
  21454. //left eye
  21455. new GUI_ELLIPSE(pWindow, ++childId, 7700, 2900, 300, 300, color+2,
  21456. FILL_SOLID);
  21457. //right eye
  21458. new GUI_ELLIPSE(pWindow, ++childId, 8300, 2900, 300, 300, color+2,
  21459. FILL_SOLID);
  21460. //nose
  21461. new GUI_LINE(pWindow, ++childId, 8000, 3200, 8000, 3600, color+2, 1);
  21462. //mouth
  21463. new GUI_LINE(pWindow, ++childId, 7900, 3800, 8100, 3800, color+2, 1);
  21464. new GUI_LINE(pWindow, ++childId, 7800, 3700, 7900, 3800, color+2, 1);
  21465. new GUI_LINE(pWindow, ++childId, 8100, 3800, 8200, 3700, color+2, 1);
  21466. //head
  21467. new GUI_ELLIPSE(pWindow, ++childId, 8000, 3200, 1400, 1600, color+1,
  21468. FILL_SOLID);
  21469.  
  21470. pWindow->Show();
  21471. }
  21472.  
  21473. void DeleteMyWindow(GUI_WINDOW *pWindow)
  21474. {
  21475. int id;
  21476. GUI_OBJECT *pObj;
  21477.  
  21478. for(id = MY_EXIT + pWindow->GetId();
  21479. pObj = pWindow->GetChildFromId(id) ;
  21480. id++) delete pObj;
  21481. delete pWindow;
  21482. }
  21483. /* End of File */
  21484.  
  21485.  
  21486.  
  21487.  
  21488.  
  21489.  
  21490.  
  21491.  
  21492.  
  21493.  
  21494.  
  21495.  
  21496.  
  21497.  
  21498.  
  21499.  
  21500.  
  21501.  
  21502.  
  21503.  
  21504.  
  21505.  
  21506.  
  21507.  
  21508.  
  21509. Two Wildcard Matching Utilities
  21510.  
  21511.  
  21512. Mike Cornelison
  21513.  
  21514.  
  21515. Mike Cornelison is a systems programmer and occasional consultant working in
  21516. Santa Clara, CA. He may be reached through the internet at
  21517. micro@Ix.netcom.com, by phone at 408-970-5294, or by fax at 408-942-9180.
  21518.  
  21519.  
  21520.  
  21521.  
  21522. Introduction
  21523.  
  21524.  
  21525. Have you ever had to switch to a new software development environment, only to
  21526. find that some of your favorite tools were missing in the new environment?
  21527. This has happened to me several times (I have been programming since 1966).
  21528. Most recently, I moved from VAX/VMS to Microsoft Windows and Windows/NT, and I
  21529. found myself missing the powerful VMS string matching and file searching
  21530. functions.
  21531. The VMS string compare function (str$match_wild) can handle any number of
  21532. wildcard characters, and find any possible match with a candidate string. This
  21533. function recognizes the wildcard character '*', which matches any number of
  21534. characters (including zero), and '%', which matches any single character. For
  21535. example, the wildcard string "*abc*def*gh%" would match all of the following
  21536. candidate strings: "abcdefghi", "XXabcXXdefXXXghX", and
  21537. "abcXdefXXghiXXXabcdefghi" (the last one is subtle).
  21538. The VMS directory search function (lib$find_file) can handle the same wildcard
  21539. notation, in both the directory tree being searched (the path), and in the
  21540. file name being searched for within the path. In addition, the notation "..."
  21541. may be used to indicate an arbitrary number of subdirectories within the path.
  21542. For example, the directory and file specification "[aa*...*bb...]ccc.*" means:
  21543. search all directories with names begining with "aa", and their subdirectory
  21544. trees, for directories with names ending in "bb". Search these directories,
  21545. and their subdirectory trees, for file names matching "ccc.*".
  21546. As it turns out, it is not very difficult to make similar tools for the
  21547. Windows and NT environments. That is the subject of this article.
  21548.  
  21549.  
  21550. MatchWild Function
  21551.  
  21552.  
  21553. This function compares a candidate string to a wildcard string -- one
  21554. containing any number of the wildcard characters '*' and '?'. The function
  21555. returns TRUE if a match is found, or FALSE if not (See Listing 1). MatchWild
  21556. makes no use of system libraries, hence it will work in any system having a C
  21557. compiler.
  21558. The logic works as follows: the two strings are scanned in parallel. For each
  21559. segment between '*'s in the wildcard string, MatchWild must be able to find a
  21560. corresponding matching segment in the candidate string. If a given comparison
  21561. fails, and the wild segment began with '*', then the function may still search
  21562. for a matching segment later in the candidate string. This fairly simple logic
  21563. will discover any possible match.
  21564. My first version of MatchWild used recursion and avoided the unfashionable
  21565. goto. It was, however, no simpler than the current version, and likely much
  21566. slower. Some benchmark execution times are included in Listing 1.
  21567.  
  21568.  
  21569. The SearchWild Function
  21570.  
  21571.  
  21572. This function (Listing 2) searches a directory tree for a desired file or set
  21573. of files. The directory tree is specified as a path name, using the
  21574. traditional notation of DOS, Windows, and Windows/NT. SearchWild's objective
  21575. is to find all possible files in all possible paths that match a given path
  21576. and file name, where both the path and file names may contain the wildcards
  21577. '*' and '?'. This function also allows a third wildcard notation, the
  21578. VMS-style "..." to indicate any number of nested subdirectories. The path and
  21579. file name notation is best clarified with an example:
  21580. d :\aaa*...\*bbb...\cc*.d??
  21581. This means: disk drive d, all top level directories matching "aaa*", all
  21582. underlying subdirectories matching "*bbb" (with any number of in-between
  21583. subdirectories), all underlying files matching "cc*.d??" (again with any
  21584. number of in-between subdirectories). The following two files would match this
  21585. specification:
  21586. d:\aaa\bbb\cc.d12
  21587. d:\aaaxx\xxxx\yyyy\xxbbb\xxx\ccxx.d23
  21588. At first glance, SearchWild seems simple to implement. After all, DOS,
  21589. Windows, and Windows/NT all offer basic directory search functions
  21590. (FindFirstFile and FindNextFile) which are capable of some wildcard handling.
  21591. Specifically, these functions accept wildcards in the last name of a path,
  21592. which may be a file name or another directory name. A program could use these
  21593. functions to iteratively search down several levels of directory names
  21594. containing wildcards, one level at a time. Using the above example, the search
  21595. would start with "d:\aaa*" to find the desired top-level directories. Within
  21596. each directory found (e.g. "aaaxx"), the search would then progress to
  21597. directories and files at the next level down, (e.g. "d:\aaaxx\*") and these
  21598. could be matched to the next desired name "*bbb", and so on. It would all be
  21599. easy, if not for the "..." notation. Implementing this last feature requires a
  21600. more sophisticated approach.
  21601. SearchWild uses recursion to make the messy logic into something almost
  21602. simple. It's entire logic is summarized as follows:
  21603. 1. Replace any "\xxx...\" notation with the equivalent "\xxx\...A" ("xxx") may
  21604. also contain the simpler wildcards '*' and '?')
  21605. 2. If no wildcards are found except in the last name (file name), then use the
  21606. OS-provided search function to find the files and return all of them to
  21607. caller. Done.
  21608. 3. Truncate the path name after the first name having any of the wildcards '*'
  21609. or '?' or "\...\"
  21610. 4. If the wildcard is not "\...\"
  21611. a. Call the OS search function to get all matching file names at this level
  21612. b. Substitute each of these names for the wildcard name, and append remaining
  21613. path\file names truncated from step #3 above
  21614. c. Call SearchWild with each of these path\file names, return all found files
  21615. to caller. This is a recursive call, since SearchWild calls itself
  21616. 5. If the wildcard is "\...\"
  21617. a. Replace "\...\" with "\". ("\...\" can mean zero or more levels of
  21618. subdirectories)
  21619. b. Call SearchWild (recursively) with this name, return all found files to
  21620. caller
  21621. c. Replace "\...\" with "\*\...\"
  21622. d. Call SearchWild (recursively) with this name, return all found files to
  21623. caller
  21624. SearchWild returns one file per call until no more files are found, then it
  21625. returns NULL. The logic depicted above is realized by going back to the
  21626. current position in the code, after each new entry from the caller.
  21627. The recursive calls to SearchWild result in efficient execution since only the
  21628. necessary directories are searched, and no others.
  21629. I have tested the code in Listing 2 for Windows/NT. It should work for DOS or
  21630. Windows 3.1, with only minor adjustments. Note that the OS functions
  21631. FindFirst/Next support multiple search contexts, which is necessary for this
  21632. method to work. SearchWild does not support multiple contexts. This could be
  21633. done, using dynamic allocation of memory for each new context. If the caller
  21634. abandons a search before it is completed, memory can be lost. Hence, another
  21635. call type is needed, to allow the caller to abandon a search and recover the
  21636. dynamic memory.
  21637. I welcome your questions or suggestions. Please contact me at the phone number
  21638. or e-mail address shown in my bio.
  21639.  
  21640. Listing 1 String matching utility
  21641. /* ------------------------------------------------------------
  21642.  
  21643.  
  21644. int MatchWild (const char * wildstr, const char * candstr)
  21645.  
  21646. Match candidate string to wildcard string containing any number
  21647. of '*' or '?' wildcard characters. The '*' matches any number of
  21648. characters, including zero characters. The '?' matches exactly
  21649. one character. Returns 1 if there is a match, else 0.
  21650.  
  21651. Benchmarks for Pentium/60:
  21652.  
  21653. WILDCARD STRING CANDIDATE STRING Microsecs.
  21654. asdfghjkl asdfghjkl 1.5
  21655. *asdfgh XXXasdfgh 2.0
  21656. asdfgh* asdfghXXX 1.8
  21657. *aaa*bbb*ccc* XaaaXXbbbcccXXX 4.1
  21658. asd??fgh??* asdXXfghXX 2.7
  21659. *aa??a*bbb XXaaXXaXXbbXaaXXabbb 5.5
  21660.  
  21661. */
  21662.  
  21663.  
  21664. int MatchWild(const char * pWild, const char * pString)
  21665. {
  21666. int ii, star;
  21667.  
  21668. new_segment:
  21669.  
  21670. star = 0;
  21671. while (pWild[0] == '*')
  21672. {
  21673. star = 1;
  21674. pWild++;
  21675. }
  21676.  
  21677. test_match:
  21678.  
  21679. for (ii = 0; pWild[ii] && (pWild[ii] != '*'); ii++)
  21680. {
  21681. if (pWild[ii] ! = pString[ii])
  21682. {
  21683. if (! pString[ii]) return 0;
  21684. if (pWild[ii] == '?') continue;
  21685. if (! star) return 0;
  21686. pString++;
  21687. goto test_match;
  21688. }
  21689. }
  21690.  
  21691. if (pWild[ii] == '*')
  21692. {
  21693. pString += ii;
  21694. pWild += ii;
  21695. goto new_segment;
  21696. }
  21697.  
  21698. if (! pString[ii]) return 1;
  21699. if (ii && pWild[ii-1] == '*') return 1;
  21700. if (! star) return 0;
  21701. pString++;
  21702.  
  21703. goto test_match;
  21704. }
  21705.  
  21706.  
  21707. /* End of File */
  21708.  
  21709.  
  21710. Listing 2 Directory search utility
  21711. /* ------------------------------------------------------------
  21712.  
  21713. char * SearchWild (const char * wpath, int & ftf)
  21714.  
  21715. Search a directory\file path containing wildcards.
  21716. Return each matching file, one file per call.
  21717. Both the directory path and file name may contain
  21718. multiple wildcards, e.g. D:\AA*\...\*BB\CC*.E??
  21719.  
  21720. * matches zero or more characters
  21721. ? matches one character
  21722. ... matches zero or more directories
  21723. *... matches one or more directories
  21724. A*... matches one or more, beginning with 'A'
  21725.  
  21726. wpath: search path, with optional wildcards
  21727. d:\dir1\dir2\... fully qualified
  21728. \dir1\dir2\... use current disk
  21729. dir1\dir2\... use current directory
  21730.  
  21731. ftf: must be 1 to initiate a new search,
  21732. do not alter for subsequent calls
  21733.  
  21734. return value:
  21735. pointer to returned file name (d:\path\file)
  21736. or NULL if no more files found
  21737.  
  21738. */
  21739.  
  21740. #include <windows.h>
  21741.  
  21742. #define DIRECTORY FILE_ATTRIBUTE_DIRECTORY
  21743. #define maxlev 20 /* max. directory levels */
  21744. #define maxp _MAX_PATH /* max. pathname\filename length */
  21745. #define maxp4 maxp + 4
  21746.  
  21747. #define RETURN(nn,mm) { reent[lev] = nn; --lev; return mm; }
  21748.  
  21749. char * SearchWild(const char * wpath, int & ftf)
  21750.  
  21751.  
  21752. {
  21753. static HANDLE ffh[maxlev];
  21754. static WIN32_FIND_DATA ffdata[maxlev];
  21755.  
  21756. static int lev = -1, reent[maxlev];
  21757. static int fstat[maxlev], ftf2[maxlev];
  21758. static char rpath[maxp4];
  21759. static char wpath1[maxlev][maxp4],
  21760. wpath2[maxlev][maxp4],
  21761. wpath3[maxlev][maxp4];
  21762.  
  21763.  
  21764. int stat, ii, jj;
  21765. char * pF, * pF1, * pF2, * pF3;
  21766.  
  21767. if (++lev >= maxlev) goto exceed_maxlev; /* track level
  21768. of reentrancy */
  21769.  
  21770. if (ftf) ftf = 0; /* 1st time flag */
  21771.  
  21772. else switch (reent[lev]) /* re-entry, resume processing */
  21773. { /* in current search context */
  21774. case 1: goto Reentl;
  21775. case 2: goto Reent2;
  21776. case 3: goto Reent3;
  21777. case 4: goto Reent4;
  21778. default: FatalAppExit(0,"reent bug");
  21779. }
  21780. ii = strlen(wpath); /* check wpath arg. */
  21781. if (ii >= maxp) goto path_toolong;
  21782.  
  21783. /* get current D:\DIRECTORY... */
  21784.  
  21785. stat = GetCurrentDirectory(maxp.wpathl[lev]);
  21786.  
  21787. if (! stat) goto no_curr_dirk;
  21788.  
  21789. /* if caller path begins "\" use curr. disk + caller path*/
  21790. if (wpath[0] == '\\')
  21791. strcpy(wpath1[lev]+2,wpath);
  21792.  
  21793. else if (wpath[2] == '\\') /* if caller path
  21794. begins "d:\" */
  21795. strcpy(wpath1[lev],wpath); /* use caller path only */
  21796.  
  21797. else /* else use current
  21798. directory */
  21799.  
  21800. { /* + "\" + caller path */
  21801.  
  21802. ii = strlen(wpath1[lev]) + strlen(wpath) + 1;
  21803. if (ii >= maxp) goto path_toolong;
  21804. strcat(wpath1[lev],"\\");
  21805. strcat(wpath1[lev],wpath);
  21806. }
  21807.  
  21808. pF = wpath1[lev];
  21809. while (pF = strstr(pF,"...\\")) /* replace any "xxx...\" */
  21810. { /* with "xxx\...\" */
  21811.  
  21812. if (pF[-1] != '\\')
  21813. {
  21814. for (ii = strlen(pF); ii; ii--) pF[ii+l] = pF[ii];
  21815. pF[0] = '\\';
  21816. }
  21817. pF += 4;
  21818. }
  21819.  
  21820. pF1 = Strchr(wpath1[lev], '*'); /*find 1st wild char. in path */
  21821. pF2 = strchr(wpath1[lev],'?');
  21822.  
  21823. pF3 = strchr(wpath1[lev], "\\...\\");
  21824. pf = wpath1[lev] + maxp;
  21825. if (pf1) pF = pF1;
  21826. if (pF2 && (pf2 < pF)) pf = pf2;
  21827. if (pF3 && (pF3 < pF)) pF = pF3;
  21828.  
  21829. if (pF < strrchr(wpath1[lev],'\\'))
  21830. goto wild_dirk; /* wild char. is in a directory */
  21831.  
  21832. /* no wild directories exist - wild filename possible */
  21833.  
  21834. /* search files using wpath1 */
  21835.  
  21836. ffh[lev] = FindFirstFile(wpathl[lev],&ffdata[lev]);
  21837. if (ffh[lev] == INVALID_HANDLE_VALUE) RETURN(0,0)
  21838. stat = fstat[lev] = 1;
  21839. Reent1:
  21840. if (fstat[lev] == 2)
  21841. stat = FindNextFile(ffh[lev].&ffdata[lev]);
  21842. fstat[lev] = 2;
  21843. if (! stat) RETURN(0,0)
  21844. if (strcmp(ffdata[lev].cFileName,".") == 0)
  21845. goto Reent1; /* ignore "." and ".." directories */
  21846. if (strcmp(ffdata[lev].cFileName,"..") == 0) goto Reentl;
  21847. pF = strrchr(wpath1[lev],'\\'); /* last"\'in search path*/
  21848. ii = strlen(ffdata[lev].cFileName);
  21849. jj = pF - wpath1[lev] + 1;
  21850. if (ii + jj > maxp) goto path_toolong;
  21851. strncpy(rpath,wpath1[lev],jj); /* build directory\filename*/
  21852. strcpy(rpath+jj,ffdata[lev].cFileName);
  21853. RETURN(1,rpath) /* return, re-enter at Reent1 */
  21854.  
  21855. /* directory has wild characters */
  21856.  
  21857. wild_dirk;
  21858. if (pF[0] == '\\') goto wild_dots0; /* found "\...\" */
  21859.  
  21860. /* 1st wild directory contains '*' or '?' */
  21861.  
  21862. strcpy(wpath2[lev],wpath1[lev]); /* wpath2 = wpath1 thru
  21863. wild dirk */
  21864. pF = wpath2[lev] + (pF - wpath1[lev]);
  21865. while (pF[0] != '\\') pF++; /* eliminate final '\' */
  21866. pF[0] = 0;
  21867. ffh[lev] =
  21868. FindFirstFile(wpath2[lev],&ffdata[lev]); /* search using wpath2 */
  21869.  
  21870. if (ffh[lev] == INVALID_HANDLE_VALUE) RETURN(0,0)
  21871. stat = fstat[lev] = 1;
  21872. Loop2;
  21873. if (fstat[lev] == 2) stat = FindNextFile(ffh[lev],&ffdata[lev];
  21874. fstat[lev] = 2;
  21875. if (! stat) RETURN(0,0)
  21876. if (!(ffdata[lev].dwfileAttributes & DIRECTORY))
  21877. goto Loop2; /* ignore non-directories */
  21878. if (strcmp(ffdata[lev].cFileName,".") == 0)
  21879. goto Loop2; /* ignore "." and ".." directories */
  21880. if (strcmp(ffdata[lev].cFileName,"..") == 0) goto Loop2;
  21881. strcpy(wpath3[lev],wpath2[lev]);
  21882.  
  21883. pF = strrchr(wpath3[Lev],'\\') + 1; /* wpath3 = wpath1
  21884. with specific */
  21885. ii = strlen(ffdata[lev].cFileName); /* dirk in place of
  21886. wild dirk */
  21887. ii += pF - wpath3[lev];
  21888. if (ii > maxp) goto path_too long;
  21889. strcpy(pF,ffdata[lev].cFileName);
  21890. pF = wpath1[lev] + (pF - wpath3[lev]);
  21891. pF = strchr(pF,'\\');
  21892. jj =strlen(pF);
  21893. if (ii + jj > maxp) goto path_toolong;
  21894. strcpy(wpath3[lev]+ii,pF);
  21895. ftf2[lev] = 1;
  21896. Reent2:
  21897. pF = SearchWild(wpath3[lev],ftf2[lev]); /* recursive search
  21898. next level */
  21899. if (! pF) goto Loop2;
  21900. RETURN(2,rpath) /* return, re-enter at Reent2 */
  21901.  
  21902. /* 1st wild directory is "\...\" */
  21903.  
  21904. wild_dots0:
  21905. strcpy(wpath2[lev],wpath1[lev]); /* wpath2 = wpath1 */
  21906. pF = strstr(wpath2[lev],"\\...\\");
  21907. for (ii = 0; pF[ii]; ii++)
  21908. pF[ii+1] = pF[ii+5]; /* repl. "\...\"
  21909. with "\" */
  21910. ftf2[lev] = 1;
  21911. Reent3:
  21912. pF = SearchWild(wpath2[lev],ftf2[lev]); /* recursive search
  21913. next level */
  21914. if (! pF) goto wild_dots1;
  21915. RETURN(3,rpath) /* return, re-enter at Reent3 */
  21916.  
  21917. wild_dots1:
  21918. strcpy(wpath2[lev],wpath1[lev]); /* wpath2 = wpath1 */
  21919. pF = strstr(wpath2[lev], "\\...\\");
  21920. for (ii = strlen(pF); ii; ii--)
  21921. pF[ii+2] = pF[ii]; /* repl. "\...\"
  21922. with "\*\...\" */
  21923. strncpy(pF, "\\*",2);
  21924. ftf2[lev] = 1;
  21925. Reent4:
  21926. pF = SearchWild(wpath2[lev],ftf2[lev]); /* recursive search
  21927. next levl */
  21928. if (! pF) RETURN(0,0)
  21929. RETURN(4,rpath) /* return, re-enter at Reent4 */
  21930.  
  21931. /* fatal error exits */
  21932.  
  21933. exceed_maxlev:
  21934. FatalAppExit(0,"SearchWild: exceed max directory levels");
  21935.  
  21936. no_curr_dirk:
  21937. FatalAppExit(0, "SearchWild: GetCurrentDirectory() failed");
  21938.  
  21939. path_toolong:
  21940. FatalAppExit(0, "SearchWild: exceed max path length");
  21941. return 0; /* dummy, required by compiler */
  21942.  
  21943. }
  21944.  
  21945. /* End of File */
  21946.  
  21947.  
  21948.  
  21949.  
  21950.  
  21951.  
  21952.  
  21953.  
  21954.  
  21955.  
  21956.  
  21957.  
  21958.  
  21959.  
  21960.  
  21961.  
  21962.  
  21963.  
  21964.  
  21965.  
  21966.  
  21967.  
  21968.  
  21969.  
  21970.  
  21971.  
  21972.  
  21973.  
  21974.  
  21975.  
  21976.  
  21977.  
  21978.  
  21979.  
  21980.  
  21981.  
  21982.  
  21983.  
  21984.  
  21985.  
  21986.  
  21987.  
  21988.  
  21989.  
  21990.  
  21991.  
  21992.  
  21993.  
  21994.  
  21995.  
  21996.  
  21997.  
  21998.  
  21999.  
  22000.  
  22001.  
  22002.  
  22003.  
  22004.  
  22005.  
  22006. Windows Programming Power with Custom Controls
  22007.  
  22008.  
  22009. Bob Swart
  22010.  
  22011.  
  22012. Bob Swart is a professional software developer and free-lance technical author
  22013. using Borland Pascak, C++, and Delphi. In his spare time he likes to watch
  22014. video tapes of Star Trek The Next Generation with his 1-year-old son Erik Mark
  22015. Pascal.
  22016.  
  22017.  
  22018. Windows Custom Controls nowadays come in (at least) two flavors: standard
  22019. custom controls and VBX-style custom controls. Windows Programming Power with
  22020. Custom Controls covers both flavors and is intended for two groups of people:
  22021. users and designers/programmers of custom controls. You don't have to be an
  22022. experienced Windows programmer to learn what custom controls are or how to use
  22023. them. The book explains in-depth the history of standard and VBX-style
  22024. controls. By reading this book you learn what the differences are between
  22025. these two. Unfortunately, no mention of OLE custom controls (OCX-style
  22026. controls, an extention of VBXs) is included.
  22027. To emphasize the difference between standard and VBX-style controls (in design
  22028. as well as in use), the book shows how to create your own custom controls in
  22029. C/C++. This is no doubt the better way to master them! To implement VBX
  22030. controls you need the VB SDK, but don't worry, even without this SDK the book
  22031. is worth reading because it contains a lot of useful information about usage
  22032. of VBX controls in applications.
  22033. The first two chapters describe the historic details of custom controls and
  22034. Windows, written in the exciting Duntemann style. The following chapters
  22035. present custom controls as ideal software components, not just "gadgets," and
  22036. show that VBX controls have use beyond Visual Basic. (For instance, VBX level
  22037. 1.0 controls can be used with Visual C++ and Borland C++, and with Delphi.)
  22038. The third chapter starts the real work, by building a framework or "skeleton"
  22039. custom control. Actually, this skeleton does nothing, but it serves as the
  22040. platform -- with already half the work done -- to build future example
  22041. controls in subsequent chapters. This chapter provides much detail, as it is
  22042. the foundation for the rest of the book.
  22043. Chapter four describes design guidelines and shows some different ways to use
  22044. custom controls in an application. This chapter is really short, being
  22045. preparatory to chapters five through eleven, where a total of seven custom
  22046. contols are implemented in both general and VBX style.
  22047.  
  22048.  
  22049. The Seven Custom Controls
  22050.  
  22051.  
  22052. Here are the seven controls presented in the book:
  22053. The Panel Control gives your programs a 3D, high-tech look and feel. This
  22054. control is presented only in the standard custom control format. As a bonus,
  22055. however, the book also presents the control with a Borland Pascal interface
  22056. unit.
  22057. The Virtual Listbox Control can display up to 32K items in a listbox, not
  22058. simply 32K of total data size, as with normal Windows listboxes.
  22059. The Pagelist Control will let you pick one or more page icons from a
  22060. horizontal list of icons.
  22061. The Browser Control is a read-only viewer that lets you examine, among other
  22062. things, database records, much like the Visual Basic Data control.
  22063. The Text File Viewer Control can view up to 32K lines, and can be expanded to
  22064. show even more lines.
  22065. The Text File Editor Control, based on the Text File Viewer, can edit up to
  22066. 32K lines. Compare that to the normal Windows Edit Control that can hold only
  22067. up to 32KB of data. This text file editor supports communication with the
  22068. clipboard and all common text editing features.
  22069. The IniData Control is a special-purpose edit control that manages Windows INI
  22070. files. It even contains a security feature that enables you to encrypt
  22071. selected items from an INI file.
  22072. The source and executables to these controls are included on the accompanying
  22073. disk. In my opinion, they are worth the price of the book by themselves!
  22074. According to the author, book purchasers can treat the source code on the disk
  22075. as a software package with one license. However, there are no royalties
  22076. imposed on run-time applications (or accompanying DLLs and VBXs) developed
  22077. with this source code. So, while you may not distribute the source code, you
  22078. may distribute DLLs, VBXs, or executables without restriction.
  22079. The appendix presents a detailed view of the controls' use. These VBX controls
  22080. will work with Borland C++, Microsoft Visual C++, and Visual Basic. The
  22081. generic version of these custom controls will work in any
  22082. Windowsdevelopmentenvironment.
  22083. The disk contains full C/C++ source code of everything discussed in the book,
  22084. plus project files for Turbo C++ and Borland C++, and makefiles for Visual
  22085. C++. The online documentation includes a windows helpfile for every example
  22086. custom control. The code is of good quality, and is made clear by intelligent
  22087. use of comments. I'm a little disapointed that almost none of the sources are
  22088. specifically C++. I wouldn't mind seeing a C++ class wrapper around the
  22089. skeleton custom control, for example. Of course, readers can easily extend the
  22090. generic C source code itself.
  22091.  
  22092.  
  22093. Do It Yourself
  22094.  
  22095.  
  22096. This book will teach you the inner workings of Windows custom controls. The
  22097. included C/C++ skeleton custom control enables you to build your own custom
  22098. controls, while the book's highly detailed examples will help you master the
  22099. entire process from start to finish. I recommend this book for all serious
  22100. windows programmers.
  22101. Title: Windows Programming Power with Custom Controls
  22102. Authors: Paul Cilwa & Jeff Duntemann
  22103. Publisher: The Coriolis Group (1994)
  22104. Pages: 496
  22105. Price: $39.93 (with 3.5" disk)
  22106. ISBN: 1-883577-00-4
  22107.  
  22108.  
  22109.  
  22110.  
  22111.  
  22112.  
  22113.  
  22114.  
  22115.  
  22116.  
  22117.  
  22118.  
  22119.  
  22120.  
  22121.  
  22122.  
  22123.  
  22124.  
  22125.  
  22126. Standard C/C++
  22127.  
  22128.  
  22129. The Header <fstream>
  22130.  
  22131.  
  22132.  
  22133.  
  22134. P.J. Plauger
  22135.  
  22136.  
  22137. P.J. Plauger is senior editor of C/C++ Users Journal. He is convener fo the
  22138. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  22139. latest books are The Draft Standard C++ Library, and Programming on Purpose
  22140. (three volumes), all published by Prentice-Hall. You can reach him at
  22141. pjp@plauger.com.
  22142.  
  22143.  
  22144.  
  22145.  
  22146. Introduction
  22147.  
  22148.  
  22149. I've been working my way through the iostreams portion of the draft Standard
  22150. C++ library, lo these many months. Interestingly enough, I've yet to get to
  22151. the part that programmers probably use the most -- reading and writing streams
  22152. of text connected to external files and devices. This installment still
  22153. doesn't get as far as cin and cout, those ubiquitous standard streams we all
  22154. know and love. But it does lay some important groundwork for them. At last we
  22155. will see how to specialize a stream buffer for controlling input and output.
  22156. The header <fstream> defines half a dozen classes. Three of these cooperate to
  22157. help you read and write files that you open by name:
  22158. filebuf, derived from streambuf to mediate access to an external stream of
  22159. characters read from or written to a file
  22160. ifstream, derived from istream to construct a filebuf object and to assist in
  22161. extracting from the stream
  22162. ofstream, derived from ostream to construct a filebuf object and to assist in
  22163. inserting into the stream
  22164. The other three classes defined in <fstream> cooperate to help you read and
  22165. write files controlled by an object of type FILE, declared in <stdio.h>:
  22166. stdiobuf, derived from streambuf to mediate access to a stream of characters
  22167. read from or written to an external stream under control of a FILE object
  22168. istdiostream, derived from istream to construct a stdiobuf object and to
  22169. assist in extracting from the stream
  22170. ostdiostream, derived from ostream to construct a stdiobuf object and to
  22171. assist in inserting into the stream
  22172. Listing 1 shows the way I chose to implement the header <fstream>. I present
  22173. it here, without explanation, for those of you who feel better looking at
  22174. concrete code. Next month, I discuss in more detail how to implement this
  22175. header.
  22176. Reading and writing files is an important part of iostreams. For many C++
  22177. programs, it is the sole means of communication between a program and the
  22178. outside world. The standard stream objects, such as cin and cout, are
  22179. conventionally associated with external streams. Even in this era of
  22180. window-oriented interfaces, reading and writing files remains important.
  22181. Communication between two programs, between a program and a window, or between
  22182. a program and a special device are all often made to look like conventional
  22183. file reads and writes. The classes defined in <fstream> are the preferred
  22184. agents for controlling such file operations.
  22185. You do not, in principle, need two sets of classes for accessing files with
  22186. iostreams. The Standard C library function fopen, declared in <stdio.h>,
  22187. associates a named file with a FILE object. The same header declares fclose,
  22188. for removing the association. You can associate a FILE object with a
  22189. stdiostream object, as described above, and you're in business. Why the extra
  22190. machinery?
  22191.  
  22192.  
  22193. A Bit of History
  22194.  
  22195.  
  22196. The answer is largely historical. The iostreams package evolved alongside the
  22197. Standard C library more than atop it. Both spent their earliest days hosted on
  22198. the UNIX operating system, a particularly friendly environment for reading and
  22199. writing files. As they have spread to other systems, both have also profited
  22200. from the influence of UNIX on more modern opeaating systems. Thus, iostreams
  22201. and the Standard C library have common heritage, and many common architectural
  22202. features, but they nevertheless grew up separately.
  22203. As a consequence, mixing iostreams and C stream operations has often led to an
  22204. uneasy alliance. A filebuf object often performs reads and writes directly,
  22205. using low-level operating system calls and managing an in-memory buffer
  22206. directly. A stdiobuf object, by contrast, typically calls on the higher-level
  22207. functions declared in <stdio.h>. The overhead per character can be much higher
  22208. and buffering can occur in two different places within the program.
  22209. C++ programmers habitually favor the former over the latter, if only for
  22210. better performance. They introduce stdiobuf objects only when obliged to mix
  22211. iostreams and C stream reads or writes to the same file. And then they fret
  22212. over the uncertainties that inevitably arise with double buffering. Input can
  22213. be consumed in greater chunks than you expect, by the two agents. Or output
  22214. from the two agents can get pasted together in bigger chunks than you intend.
  22215. Often, the only safe fix is to make stdiobuf operations completely unbuffered.
  22216. And that often penalizes performance even more.
  22217. The draft C++ Standard addresses these historical problems:
  22218. It defines the semantics of class filebuf as if it performs reads and writes
  22219. through a FILE object. The draft C++ Standard thus rides atop the detailed
  22220. descriptions of file operations from the C Standard. And it gives quick
  22221. answers to many questions about the effect of mixing iostreams and C stream
  22222. operations.
  22223. It defines class filebuf in such a way that no FILE object is directly
  22224. visible. The draft C++ Standard thus permits an implementation of filebuf atop
  22225. the Standard C library, without mandating it.
  22226. It retains class stdiobuf, which is defined explicitly in terms of a visible
  22227. FILE object. Operations are unbuffered by default, so programs have no
  22228. synchronization surprises.
  22229. It nevertheless provides member functions to control whether stdiobuf
  22230. operations can be buffered, if performance is more important to you than tight
  22231. synchronization between iostreams and C stream operations.
  22232. It specifies that the standard objects cin, cout, cerr, and clog behave as if
  22233. they designate unbuffered stdiostream objects, without requiring exactly that
  22234. implementation.
  22235. The basic idea is to better define the relationship between iostreams and C
  22236. stream operations, and to provide as a default less surprising semantics for
  22237. mixed operations. Nevertheless, the draft C++ Standard does not require that
  22238. existing implementations of iostreams be rewritten purely in terms of calls to
  22239. Standard C library functions.
  22240. One example of shared semantics is the way you open a file by name. The
  22241. filebuf member function open(const char *, ios::openmode) is the agent that
  22242. does the job. It effectively calls fopen, declared in <stdio.h>. To do so, it
  22243. must map its second argument to the kind of mode string acceptable to fopen.
  22244. Thus, for example, ios::in becomes "r". The member function is obliged to
  22245. accept only those combinations of ios::openmode elements for which a
  22246. corresponding mode string is defined.
  22247. Another example is the semantics of stream positioning. The Standard C library
  22248. permits a FILE object to mediate both reads and writes, but only in a rather
  22249. stylized way. At any given time, the input stream may be readable or the
  22250. output stream may be writable, but not both. (Of course, it is also possible
  22251. that neither stream is available.) Typically, you have to perform a
  22252. stream-positioning operation to switch between reading and writing. Equally,
  22253. only one stream position is maintained for both reading and writing. And an
  22254. arbitrary stream position requires the unspecified information stored in an
  22255. fpos_t object, not just a streamoff value.
  22256. Iostreams implicitly acknowledges these limitations in several ways. By
  22257. inheritance, it has the same semantic limitations on switching between
  22258. extracting and inserting. (Opinions differ on this point within the Committee,
  22259. however.) If you attempt to position either stream in a filebuf object, you
  22260. position both at once. And the semantics of class streampos reflect the
  22261. realities of the restrictions on fpos_t objects. (See "Standard C: The Header
  22262. <streambuf>," CUJ, June 1994.)
  22263. I don't want to paint too rosy a picture, however. Several Committee members
  22264. begrudge almost any concession to the Standard C library in the area of file
  22265. operations. They may accept the narrow need for defining the semantics of the
  22266. two libraries when they work together. But some feel that the Standard C++
  22267. library is better off being defined de novo in this area. I personally don't
  22268. see how to introduce new semantics for iostreams without massively
  22269. complicating the description of, say, stdiobuf. Others, however, may find a
  22270. way over time.
  22271.  
  22272.  
  22273. Recent Changes
  22274.  
  22275.  
  22276. The description of <fstream> depends heavily on references to functions and
  22277. types declared in <stdio.h>, for reasons I indicated above. Please note once
  22278. more, however, that such language makes no promises about how classes in this
  22279. header are actually implemented. The draft C++ Standard often says, for
  22280. example, that function A calls function B. Generally, this means only that A
  22281. behaves as if it calls B. If you can write a portable program that can detect
  22282. whether the call occurs -- such as to a virtual member function that you can
  22283. override -- the call may be obligatory. Otherwise, don't be surprised if an
  22284. interactive debugger fails to detect an actual call.
  22285. As I mentioned in conjunction with the header <streambuf> class streambuf has
  22286. an added virtual member function. The public access function for it is called
  22287. showmany. It endeavors to tell you how many characters you can safely extract
  22288. with no fear of blocking while waiting for additional input. The derived
  22289. classes filebuf and stdiobuf should have nontrivial overrides for this virtual
  22290. member function, on systems that can supply the needed information.
  22291.  
  22292. Once again, I note that a major change to all of iostreams is the addition of
  22293. wide-character streams. The classes filebuf and stdiobuf become template
  22294. classes parameterized by the type of the stream element. One instantiation,
  22295. for type char, has essentially the same functionality as described here.
  22296. Another, for type wchar_t, supports streams of elements from some large
  22297. character set. Classes ifstream, ofstream, istdiostream, and ostdiostream
  22298. change along similar lines.
  22299. As before, I continue to present an implementation written just to handle char
  22300. streams. It illustrates all the basic issues, without the additional
  22301. complexity of templates.
  22302. Finally, I must report that the classes stdiobuf, istdiostream, and
  22303. ostdiostream have been voted out of the draft C++ Standard. Since they occur
  22304. widely in existing practice, however, I suspect that many vendors will still
  22305. choose to supply them. I, for one, am not quick to burn this useful bridge
  22306. between the worlds of C and C++.
  22307.  
  22308.  
  22309. Using <fstream>
  22310.  
  22311.  
  22312. You include the header <fstream> to make use of any of the classes ifstream,
  22313. ofstream, filebuf, istdiostream, ostdiostream, or stdiobuf. Objects of these
  22314. classes let you read and write conventional files. You can open files by name
  22315. and control them, or control files already opened under control of objects of
  22316. type FILE. For each approach, you can choose among three patterns of access:
  22317. read only
  22318. write only
  22319. read/write
  22320. I deal with each of these options in turn, first for files you open by name.
  22321.  
  22322.  
  22323. Read Only, By Name
  22324.  
  22325.  
  22326. If all you want to do is open and read an existing text file whose name you
  22327. know, construct an object of class ifstream. If you know at construction time
  22328. what null-terminated file name s you wish to use, you can write:
  22329. ifstream fin(s);
  22330. if (fin.is_open())
  22331. <file opened successfully>
  22332. If the file is not opened successfully, subsequent extractions will fail.
  22333. Note, however, that the conventional tests for failure, !fin or fin != 0, will
  22334. not be false until after you essay such an extraction. That's why I encourage
  22335. you to make the explicit test fin. is_open() immediately after the object is
  22336. constructed.
  22337. The resultant stream buffer (pointed at by fin.rdbuf()) does not support
  22338. insertions. You can, however, close any currently open file, then open the
  22339. file s2 for reading with the two calls:
  22340. fin.close(), fin.open(s2);
  22341. Naturally, you should once again test whether the open succeeded, as above.
  22342. The stream position is reset to the beginning of the newly opened stream. (And
  22343. the resultant stream buffer still does not support insertions.)
  22344. You can also construct an ifstream object with no open file, using the default
  22345. constructor. Presumably, you would later open an existing text file for
  22346. reading, as in:
  22347. ifstream fin;
  22348. fin.open(s);
  22349. if (fin.is_open())
  22350. <file opened successfully>
  22351. Destroying an ifstream object closes any open file associated with it.
  22352. The code I have shown so far always opens a text file, for reading only. A
  22353. text file can be subject to a certain amount of interpretation, such as
  22354. mapping the sequence carriage return/line feed to just line feed (newline). A
  22355. binary file, on the other hand, delivers each byte from the file unchanged as
  22356. a char value. To read a binary file, to make a file writable as well, or to
  22357. invoke various other options when you open a file, you have to specify an
  22358. explicit open-mode argument. (Naturally enough, it has type ios::openmode.)
  22359. For all member functions that take a file-name argument s, the open-mode mode
  22360. immediately follows. The first example, above, is actually equivalent to:
  22361. ifstream fin(s, ios::in);
  22362. if (fin.is_open())
  22363. <file opened successfully>
  22364. You have a number of options for the value of mode:
  22365. ios::in, to open an existing text file for reading
  22366. ios::out / ios::trunc, to create a text file or to open and truncate an
  22367. existing text file for writing
  22368. ios::out / ios::app, to create a text file or to open an existing text file
  22369. for writing, where each write occurs at the end of the file
  22370. ios::in / ios::binary, to open an existing binary file for reading
  22371. ios::out / ios::trunc / ios::binary, to create a binary file or to open and
  22372. truncate an existing binary file for writing
  22373. ios::out / ios::app / ios::binary, to create a binary file or to open an
  22374. existing binary file for writing, where each write occurs at the end of the
  22375. file
  22376. ios::in / ios::out, to open an existing text file for reading and writing
  22377. ios::in / ios::out / ios::trunc, to create a text file or to open and truncate
  22378. an existing text file for reading and writing
  22379. ios::in / ios::out / ios::app, to create a text file or to open an existing
  22380. text file for reading and writing, where each write occurs at the end of the
  22381. file
  22382. ios::in / ios::out / ios::binary, to open an existing binary file for reading
  22383. and writing
  22384. ios::in / ios::out / ios::trunc / ios::binary, to create a binary file or to
  22385. open and truncate an existing binary file for reading and writing
  22386. ios::in / ios::out / ios::app / ios::binary, to create a binary file or to
  22387. open an existing binary file for reading and writing, where each write occurs
  22388. at the end of the file
  22389. If you also set ios::ate in mode, the file is positioned at end-of-file
  22390. immediately after it is opened.
  22391.  
  22392.  
  22393. Write Only, By Name
  22394.  
  22395.  
  22396. If all you want to do is create a new text file -- or truncate an existing
  22397. text file -- then open it for writing, construct an object of class ofstream
  22398. to control insertions into it. You can write:
  22399. ofstream fout(s);
  22400. if (fout.is_open())
  22401. <file opened successfully>
  22402.  
  22403. then insert into fout just like any other output stream. As with class
  22404. ifstream, you can follow the file name s with a mode argument. If you omit the
  22405. mode argument, as above, it defaults to ios::out.
  22406. You can also construct an ofstream object with the default constructor and
  22407. later create it for writing, as in:
  22408. ofstream fout;
  22409. fout.open(s);
  22410. if (fout.is_open())
  22411. <file opened successfully>
  22412. In either case, the resultant stream buffer (pointed at by fout. rdbuf()) does
  22413. not support extractions. And, of course, destroying an ofstream object closes
  22414. any open file associated with it.
  22415.  
  22416.  
  22417. Read/Write, By Name
  22418.  
  22419.  
  22420. If you want to open a file that you can read as well as write, you need two
  22421. objects to control the input and output streams. The classes ifstream and
  22422. ofstream are highly symmetric, at least in this regard. Thus, you have three
  22423. equally valid ways to do the job. If you don't want to open a file initially,
  22424. you can write:
  22425. ifstream ifile;
  22426. ostream ofile(ifile.rdbuf());
  22427. or:
  22428. ofstream ofile;
  22429. istream ifil e(ofile.rdbuf());
  22430. or:
  22431. filebuf fb;
  22432. istream ifile(&fb);
  22433. ostream ofile(&fb);
  22434. All approaches cause ifile to control the input stream and ofile to control
  22435. the output stream.
  22436. You can also open a file s in each of these three cases. Since the default
  22437. values for the mode argument rarely make sense here, I show the argument
  22438. explicitly in each case:
  22439. ifstream ifile(s, mode);
  22440. ostream ofile(ifile.rdbuf());
  22441. if (ifile.is_open())
  22442. <file opened successfully>
  22443. or:
  22444. ofstream ofile(s, mode);
  22445. istream ifile(ofile.rdbuf());
  22446. if (ofile.is_open())
  22447. <file opened successfully>
  22448. or:
  22449. filebuf fb;
  22450. istream ifile(&fb);
  22451. ostream ofile(&fb);
  22452. if (fb.open(s, mode))
  22453. <file opened successfully>;
  22454. Note that the last test for a successful open differs from the earlier ones.
  22455. As usual, when the filebuf object is destroyed, any open file associated with
  22456. it is closed.
  22457.  
  22458.  
  22459. Controlling C Streams
  22460.  
  22461.  
  22462. The classes istdiostream, ostdiostream, and stdiobuf provide additional
  22463. capability within the header <fstream>. They let you control files already
  22464. opened under control of an object of type FILE. For example, the function
  22465. fopen, declared in <stdio.h>, returns a non-null pointer to FILE when it
  22466. successfully opens a file. Numerous other functions, declared in the same
  22467. header, support C stream reads and writes to the opened file.
  22468. The same header also declares three well known objects of type pointer to FILE
  22469. that control the three standard streams:
  22470. stdin, controlling the standard input stream
  22471. stdout, controlling the standard output stream
  22472. stderr, controlling the standard error stream
  22473. The header <iostream> declares several istream and ostream objects that work
  22474. in concert with these objects to support iostreams operations on the standard
  22475. streams. You can nevertheless use the facilities in <fstream> to control, say,
  22476. stdout with an additional object you construct.
  22477. As usual, there are three patterns of access to discuss: read only, write
  22478. only, and read/write. I cover them in order.
  22479.  
  22480.  
  22481. Read Only
  22482.  
  22483.  
  22484. If all you want to do is read a stream controlled by a FILE object, construct
  22485. an object of class istdiostream. You must know at construction time the
  22486. argument value pf, of type pointer to FILE. You can write:
  22487. istdiostream fin(pf);
  22488.  
  22489. If pf is a null pointer, or if the stream it controls cannot be read, all
  22490. subsequent insertion operations will fail. You cannot, however, test whether
  22491. fin is associated with an open file.
  22492. When fin is destroyed, the stream *pf is not closed. Nor should you close the
  22493. stream, by calling fclose(pf), before fin is destroyed. The call discredits
  22494. pf, so even a subsequent attempt to access the pointer itself can cause a
  22495. program to terminate abnormally. Worse, subsequent attempts to control the
  22496. file may do all sorts of insane things that are not diagnosed.
  22497. You can control the degree of buffering within fin. Initially, fin.buffered()
  22498. returns zero, indicating that no buffering occurs. Put simply, you can
  22499. alternate the calls fin.get() and fgetc(pf) and read alternate characters from
  22500. the file.
  22501. Once you call fin.buffered(1), however, fin.buffered(1) returns a nonzero
  22502. value. Thereafter, buffering may occur. Put simply, the call fin.get() may
  22503. encourage the stream buffer associated with fin to gobble an arbitrary number
  22504. of characters, not just the one you requested. A subsequent call to fgetc(pf)
  22505. will not necessarily deliver the next character you would expect.
  22506. If you resist the temptation to access *pf directly, buffering causes no
  22507. problems. On the contrary, it offers the controlling stream buffer the
  22508. opportunity to improve performance, sometimes considerably. A wise rule of
  22509. thumb, therefore, is never to enable buffering for a file accessed both via a
  22510. stream buffer and via C stream function calls. If the stream buffer is the
  22511. sole agent accessing the file, always enable buffering.
  22512.  
  22513.  
  22514. Write Only
  22515.  
  22516.  
  22517. If all you want to do is write a stream controlled by a FILE object, construct
  22518. an object of class ostdiostream. You must know at construction time the
  22519. argument value pf, of type pointer to FILE. You can write:
  22520. ostdiostream fout(pf);
  22521. If pf is a null pointer, or if the stream it controls cannot be written, all
  22522. subsequent insertion operations will fail. As with an istiodstream object, you
  22523. cannot test whether fout is associated with an open file. The same remarks
  22524. also apply about not closing the file until fout is destroyed. Equally, the
  22525. same considerations apply about when to buffer, or not to buffer, a stream
  22526. associated with an ostdiostream object.
  22527.  
  22528.  
  22529. Read/Write
  22530.  
  22531.  
  22532. Finally, if you want to both read and write a stream controlled by a FILE
  22533. object, you need two objects to control the input and output streams. The
  22534. classes istdiostream and ostdiostream are highly symmetric. Thus, you have
  22535. three equally valid ways to do the job:
  22536. istdiostream ifile(pf);
  22537. ostream ofile(ifile.rdbuf());
  22538. or:
  22539. ostdiostream ofile(pf);
  22540. istream ifile(ofile.rdbuf());
  22541. or:
  22542. stdiobuf sb(pf);
  22543. istream ifile(&sb);
  22544. ostream ofile(&sb);
  22545. In the third case, you enable buffering by calling sb. buffered(1).
  22546.  
  22547.  
  22548. File Positioning
  22549.  
  22550.  
  22551. Earlier, I discussed the limitations on positioning within files. Your safest
  22552. bet, as always, is to memorize a file position you want to return to, as an
  22553. object of type streampos. Later on in the program, while the file is still
  22554. open, you can use the value stored in this object to return to the memorized
  22555. file position.
  22556. For a binary file that is not too large, you can represent a stream position
  22557. as an object of type streamoff. You can thus perform arithmetic, on byte
  22558. displacements from the beginning of a file, to determine new stream positions.
  22559. The UNIX operating system represents text files the same as binary. Hence, it
  22560. extends the same latitude in stream positioning to all files, not just binary.
  22561. But few other systems share this simplicity. Don't write portable code that
  22562. counts on it.
  22563. A file opened both for reading and writing requires intervening
  22564. stream-positioning requests when switching from reading to writing, or back.
  22565. Again, some systems may relax this requirement, but don't count on it in a
  22566. portable program.
  22567. Finally, the streambuf virtual member functions setbuf and sync are given
  22568. non-trivial semantics in the derived class filebuf. The former, however, is
  22569. defined in terms of the function setvbuf, declared in <stdio.h>, which does
  22570. not itself promise much. And the latter is generally called as often as
  22571. necessary in the normal course of business. I recommend, therefore, that you
  22572. not call either pubsetbuf or pubsync, the public member functions that call
  22573. these virtual member functions on your behalf.
  22574. This article is excerpted in part from P.J. Plauger, The Draft Standard C++
  22575. Library, (Englewood Cliffs, N.J.: Prentice-Hall, 1995).
  22576.  
  22577. Listing 1 The header <fstream>
  22578. // fstream standard header
  22579. #ifndef _FSTREAM_____LINEEND____
  22580. #define _FSTREAM_____LINEEND____
  22581. #include <istream>
  22582. #include <ostream>
  22583. // class filebuf
  22584. struct _Filet;
  22585. class filebuf : public streambuf {
  22586. public:
  22587. filebuf(_Filet *_F = 0)
  22588. {_Init(_F); }
  22589. filebuf(ios::_Uninitialized)
  22590. : streambuf(ios::_Noinit) {}
  22591. virtual ~filebuf();
  22592. bool is_open() const
  22593. {return ((_File != 0)); }
  22594. filebuf *open(const char *, ios::openmode);
  22595. filebuf *open(const char *_N, ios::open_mode _M)
  22596.  
  22597. {return (open(_N, (ios::openmode)_M)); }
  22598. filebuf *close();
  22599. protected:
  22600. virtual int overflow(int = EOF);
  22601. virtual int pbackfail(int = EOF);
  22602. virtual int underflow();
  22603. virtual int uflow();
  22604. virtual streamsize xsgetn(char *, streamsize);
  22605. virtual streamsize xsputn(const char *, streamsize);
  22606. virtual streampos seekoff(streamoff, ios::seekdir,
  22607. ios::openmode = (ios::openmode)(ios::in ios::out));
  22608. virtual streampos seekpos(streampos,
  22609. ios::openmode = (ios::openmode)(ios::in ios::out));
  22610. virtual streambuf *setbuf(char *, streamsize);
  22611. virtual in sync();
  22612. _Filet *_Init(_Filet * = 0, bool = 0);
  22613. private:
  22614. bool _Closef;
  22615. _Filet *_File;
  22616. };
  22617. // class ifstream
  22618. class ifstream : public istream {
  22619. public:
  22620. ifstream()
  22621. : istream(&_Fb) {}
  22622. ifstream(const char *_S, openmode _M = in)
  22623. : istream(&_Fb) {_Fb.open(_S, _M); }
  22624. virtual ~ifstream();
  22625. filebuf *rdbuf() const
  22626. {return ((filebuf *)&_Fb); }
  22627. bool is_open() const
  22628. {return (_Fb.is_open()); }
  22629. void open(const char *_S, openmode _M = in)
  22630. {if (_Fb.open(_S, _M) == 0)
  22631. setstate(failbit): }
  22632. void open(const char *_S, open_mode _M)
  22633. {open(_S, (openmode)_M); }
  22634. void close()
  22635. {if (_Fb.close() == 0)
  22636. setstate(failbit); }
  22637. private:
  22638. filebuf _Fb;
  22639. };
  22640. // class ofstream
  22641. class ofstream : public ostream {
  22642. public:
  22643. ofstream()
  22644. : ostream(&_Fb) {}
  22645. ofstream(const char *_S, openmode _M = out trunc)
  22646. : ostream(&_Fb) {_Fb.open(_S, _M); }
  22647. virtual ~ofstream();
  22648. filebuf *rdbuf() const
  22649. {return ((filebuf *)&_Fb); }
  22650. bool is_open() const
  22651. {return (_Fb.is_open()); }
  22652. void open(const char *_S, openmode _M = out trunc)
  22653. {if (_Fb.open(_S, _M) == 0)
  22654. setstate(failbit); }
  22655. void open(const char *_S, open_mode _M)
  22656.  
  22657. {open(_S, (openmode)_M); }
  22658. void close()
  22659. {if (_Fb.close() == 0)
  22660. setstate(failbit); }
  22661. private:
  22662. filebuf _Fb;
  22663. };
  22664. // class stdiobuf
  22665. class stdiobuf : public filebuf {
  22666. public:
  22667. stdiobuf(_Filet *_F)
  22668. : filebuf(_F), _Is_buffered(0) {}
  22669. virtual ~stdiobuf();
  22670. bool buffered() const
  22671. {return (_Is_buffered); }
  22672. void buffered(bool _F)
  22673. {Is_buffered = _F; }
  22674. private:
  22675. bool _Is_buffered;
  22676. };
  22677. // class istdiostream
  22678. class istdiostream : public istream {
  22679. public:
  22680. istdiostream(_Filet *_F)
  22681. : istream(&_Fb), _Fb(_F) {}
  22682. virtual ~istdiostream();
  22683. stdiobuf *rdbuf() const
  22684. {return ((stdiobuf *)&_Fb); }
  22685. bool buffered() const
  22686. {return (_Fb.buffered()); }
  22687. void buffered(bool _F)
  22688. {_Fb.buffered(F); }
  22689. private:
  22690. stdiobuf _Fb;
  22691. };
  22692. // class ostdiostream
  22693. class ostdiostream : public ostream {
  22694. public:
  22695. ostdiostream(_Filet *_F)
  22696. : ostream(&_Fb), _Fb(_F) {}
  22697. virtual ~ostdiostream();
  22698. stdiobuf *rdbuf() const
  22699. {return ((stdiobuf *)&_Fb); }
  22700. bool buffered() const
  22701. {return (_Fb.buffered()); }
  22702. void buffered(bool _F)
  22703. {_Fb.buffered(_F); }
  22704. private:
  22705. stdiobuf _Fb;
  22706. };
  22707. #endif /* _FSTREAM_ */
  22708.  
  22709.  
  22710.  
  22711.  
  22712.  
  22713.  
  22714.  
  22715.  
  22716.  
  22717.  
  22718.  
  22719.  
  22720.  
  22721.  
  22722.  
  22723.  
  22724.  
  22725.  
  22726.  
  22727.  
  22728.  
  22729.  
  22730.  
  22731.  
  22732.  
  22733.  
  22734.  
  22735.  
  22736.  
  22737.  
  22738.  
  22739.  
  22740.  
  22741.  
  22742.  
  22743.  
  22744.  
  22745.  
  22746.  
  22747.  
  22748.  
  22749.  
  22750.  
  22751.  
  22752.  
  22753.  
  22754.  
  22755.  
  22756.  
  22757.  
  22758.  
  22759.  
  22760.  
  22761.  
  22762.  
  22763.  
  22764.  
  22765.  
  22766.  
  22767.  
  22768.  
  22769.  
  22770.  
  22771.  
  22772.  
  22773.  
  22774.  
  22775.  
  22776.  
  22777.  
  22778.  
  22779.  
  22780. Code Capsules
  22781.  
  22782.  
  22783. A Better C
  22784.  
  22785.  
  22786.  
  22787.  
  22788. Chuck Allison
  22789.  
  22790.  
  22791. Chuck Allison is a regular columnist with CUJ and a Senior Software Engineer
  22792. in the Information and Communication Systems Department of the Church of Jesus
  22793. Christ of Latter Day Saints in Salt Lake City. He has a B.S. and M.S. in
  22794. mathematics, has been programming since 1975, and has been teaching and
  22795. developing in C since 1984. His current interest is object-oriented technology
  22796. and education. He is a member of X3J16, the ANSI C++ Standards Committee.
  22797. Chuck can be reached on the Internet at 72640.1507@compuserve.com.
  22798.  
  22799.  
  22800. Bjarne Stroustrup, the inventor of C++, has suggested three ways to look at
  22801. C++:
  22802. 1) as a better C
  22803. 2) as a language that supports data abstraction, and
  22804. 3) as an object-oriented programming language
  22805. Because C++ truly is all of these things, it is somewhat more complex than
  22806. most popular programming languages, especially the other object-oriented ones.
  22807. The proponents of SmallTalk, Eiffel and CLOS (object-oriented Common LISP)
  22808. engage in much religious debate about purity, simplicity, and the "true"
  22809. definition of the term "object-oriented." But C++ is in fact a multi-paradigm
  22810. language -- it supports the traditional procedural style of programming, like
  22811. C and Pascal do; like Ada it supports data abstraction and generics
  22812. (templates); and it supports inheritance and polymorphism, like all the other
  22813. object-oriented languages. All this may make for somewhat of an impure
  22814. programming language, but it also makes C++ the more practical choice for
  22815. production programming. C++ unquestionably gives the best performance,
  22816. functions well in mixed-language environments (not just with C, but with
  22817. FORTRAN, COBOL, and other languages, as well), and does not require the
  22818. enormous resources that Smalltalk, Eiffel and LISP do (the latter being
  22819. environments, not just compile-and-link processes).
  22820. In this article I explore the non-object-oriented features of C++ that make it
  22821. "a Better C." Remember, however, that C++ came about by adding classes to C --
  22822. in other words, by adding data abstraction support to C. It is difficult to
  22823. motivate some of the Better-C features without showing their class-based
  22824. origins. I therefore use the class mechanism of C++ liberally.
  22825.  
  22826.  
  22827. The Type System
  22828.  
  22829.  
  22830. Perhaps the most important thing to understand about C++ is its devotion to
  22831. type safety. The OO languages mentioned above are very weakly typed, if at
  22832. all, because they perform error-checking mainly during execution. C++, on the
  22833. other hand, requires you to declare the type of every variable, and the types
  22834. of the parameters and the return types of every function. It fastidiously
  22835. checks your usage of these objects at compile time. It is type safety, more
  22836. than anything else, that makes C++ a Better C, and the most reasonable choice
  22837. for most programming tasks.
  22838.  
  22839.  
  22840. Function Prototypes
  22841.  
  22842.  
  22843. ANSI-C-style function prototypes are not optional in C++. In fact, the
  22844. prototype mechanism was invented for C++ before the ANSI C committee adopted
  22845. it. You must either declare or fully define each function before its first
  22846. use. The compiler will check each function invocation for the correct number
  22847. and type of arguments. In addition, it will perform automatic conversions
  22848. where they apply. The program in Listing 1 shows a common error that occurs in
  22849. C when you don't use prototypes. The function dprint expects a double
  22850. argument. Without knowing dprint's prototype, the compiler doesn't know that
  22851. the call dprint(123) is an error. When you provide the prototype for dprint,
  22852. the compiler automatically converts 123 to a double for you (see Listing 2).
  22853. Since C++ allows types that you define to behave like built-in types, it
  22854. allows implicit conversions for user-defined types as well. Since the
  22855. constructor for struct A in Listing 3 expects a double argument, the compiler
  22856. automatically converts the integer 1 to a double in the definition for a. The
  22857. call f(2) generates the equivalent of the following actions:
  22858. 1. convert 2 to a double
  22859. 2. initialize a temporary A object with the value 2.0
  22860. 3. pass that object to f
  22861. In other words, the compiler generates code equivalent to:
  22862. f(A(double(2)) );
  22863. Note C++'s function-style cast syntax. The expression
  22864. double(2)
  22865. is equivalent to
  22866. (double) 2
  22867. Only one implicit user-defined conversion is allowed in any expression,
  22868. however. The program in Listing 4 requires you to provide a B object to
  22869. initialize an A object. A B object in turn requires a double, because its only
  22870. constructor is B::B(double). The expression
  22871. A a(1)
  22872. becomes:
  22873. A a(B(double(1)))
  22874. which has only one user-defined conversion. The expression f(3), however, is
  22875. invalid, because it would require the compiler to provide two automatic
  22876. user-defined conversions:
  22877. // Can't do both an A and a B
  22878. // conversion implicitly
  22879. f(A(B(double(3))) // invalid
  22880. The expression f(B(3)) is okay, because you explicitly requested the
  22881. conversion B(double(3)), so the compiler provides only the remaining
  22882. conversion to A.
  22883.  
  22884.  
  22885. Type-Safe Linkage
  22886.  
  22887.  
  22888. C++ can even detect improper function calls across compilation units. The
  22889. program in Listing 5 calls a function in Listing 6. When compiled as a C
  22890. program, the situation is analogous to Listing 1, giving the output:
  22891.  
  22892. f:0.000000
  22893. C has no way of knowing that the f's are different. The conventional
  22894. work-around is to put the correct prototype in a header file that all
  22895. compilation units include. In C++, a function call will only link with a
  22896. function that has the same signature, which is the combination of the function
  22897. name and its sequence of argument types. When compiled as a C++ program, the
  22898. output of Listing 5 and Listing 6 from Borland C++ is
  22899. Error: Undefined symbol f(int) in module safe1.cpp
  22900. Most compilers achieve this type-safe linkage by encoding the function's
  22901. signature along with its name, a technique often referred to as function name
  22902. encoding, name decorating, or my favorite, name mangling. For example, the
  22903. function f(int) might appear to the linker as
  22904. f__Fi // f is a function
  22905. // taking an int
  22906. but f(double) would be
  22907. f__Fd // f is a function
  22908. // taking a double
  22909. Since the names are different, the linker can't find f(int) in this example,
  22910. and reports an error.
  22911.  
  22912.  
  22913. References
  22914.  
  22915.  
  22916. Since C passes function parameters by value, passing large structures to
  22917. functions can waste a lot of time and stack space. The typical work-around is
  22918. to pass a pointer. For example, if struct Foo is a large record structure, you
  22919. can do something like the following:
  22920. void f(struct Foo *fp)
  22921. {
  22922. /* Access the structure
  22923. through fp */
  22924. fp->x=...
  22925. etc.
  22926. }
  22927. You have to pass the address of a struct Foo in order to use this function, of
  22928. course:
  22929. struct Foo a;
  22930. ...
  22931. f(&a);
  22932. The C++ reference mechanism is a notational convenience that saves you the
  22933. bother of providing explicit indirection of pointer variables. In C++, you can
  22934. render the above code as:
  22935. void f(Foo &fr)
  22936. {
  22937. /* Access members directly */
  22938. fr.x = ...
  22939. etc.
  22940. }
  22941. You can now call f without using the address-of operator, like this:
  22942. Foo a;
  22943. ...
  22944. f(a);
  22945. The ampersand in the prototype for f instructs the compiler to pass its
  22946. argument by reference, which in effect takes care of all the indirection for
  22947. you. For you Pascal programmers, reference parameters are equivalent to VAR
  22948. parameters.
  22949. Call-by-reference means that any changes you make to a function parameter
  22950. affect the original argument in the calling program. Thus, you can write a
  22951. swap function (not a macro) that actually works (see Listing 7). If you don't
  22952. plan on modifying a reference argument, declare it a reference-to-const, like
  22953. I did in Listing 4. A reference-to-const argument has the safety and
  22954. notational convenience of call-by-value, and the efficiency of
  22955. call-by-reference.
  22956. As Listing 8 illustrates, you can also return an object from a function by
  22957. reference. It may look strange to have a function call on the left-hand side
  22958. of an assignment, but this comes in handy when overloading operators
  22959. (especially operator= and operator[] when used as member functions).
  22960.  
  22961.  
  22962. Type-Safe I/O
  22963.  
  22964.  
  22965. I'm sure every C programmer has been bitten by using incorrect format
  22966. descriptors in printf. printf has no way to check if the data items you pass
  22967. it match your format string. How often have you done something like the
  22968. following, only to discover the problem at run time:
  22969. double d;
  22970. ...
  22971. printf("%d\n",d); /* should've used
  22972. %f */
  22973. The C++ IOStreams library, on the other hand, uses the object's type to
  22974. determine the proper formatting:
  22975. double d;
  22976. ...
  22977. cout << d << endl; // can't fail
  22978. There is no way for the output stream to misinterpret your value, If you want
  22979. to print floating-point numbers with a fixed precision, you can say so just
  22980. once:
  22981. double x = 1.5, y = 2.5;
  22982. cout.precision(2); // Show 2 decimals
  22983. cout.setf(ios::showpoint); // Preserve trailing 0's
  22984.  
  22985. cout << x << endl; // prints 1.50
  22986. cout << y << endl; // prints 2.50
  22987. The token endl is a manipulator, a special object you insert into a stream to
  22988. create a side effect -- in this case, to start a new line and flush the output
  22989. buffer. For more information on IOstreams, see the Code Capsule in the July
  22990. 1993 issue of CUJ.
  22991.  
  22992.  
  22993. Function Overloading and Templates
  22994.  
  22995.  
  22996. The swap function in Listing 7 is useful only if you want to swap integers.
  22997. What if you want to swap two objects of any built-in type? C++ allows you to
  22998. define multiple functions of the same name, as long as their signatures are
  22999. different. Therefore you can define a swap for all built-in types:
  23000. void swap(char &, char &);
  23001. void swap(int &, int &);
  23002. void swap(long &, long &);
  23003. void swap(float &, float &);
  23004. void swap(double &, double &);
  23005. etc.
  23006. You can then call swap for any two objects of the same built-in type. If you
  23007. were to implement each of these functions, however, before long you would
  23008. discover that you were doing the same thing over and over -- the only thing
  23009. that changes is the type of the objects you want to swap. To save tedium and
  23010. the chance of making a silly mistake, you can define a function template
  23011. instead. As Listing 9 demonstrates, you preface the function with the phrase
  23012. template<class T>
  23013. which says that in the code that follows, the token T stands for an arbitrary
  23014. data type, either built-in or user-defined. You then replace all occurrences
  23015. of the data type of the objects to be swapped with the template parameter T.
  23016. When the compiler sees a call to swap, it instantiates the appropriate
  23017. function, inferring the type from the type of the operands. In other words,
  23018. when the compiler sees
  23019. swap(i ,j);
  23020. it actually generates the code for swap(int &, int &), as if you had created
  23021. it yourself (but without human error).
  23022.  
  23023.  
  23024. Operator Overloading
  23025.  
  23026.  
  23027. You can also overload operators in C++. For example, suppose you define a
  23028. complex number data type as:
  23029. struct complex
  23030. {
  23031. double real, imag;
  23032. };
  23033. It would be quite convenient if you could use infix notation for adding
  23034. complex numbers, such as:
  23035. complex c1, c2;
  23036. ...
  23037. complex c3 = c1 + c2;
  23038. Operator overloading enables you to do just this. When the compiler encounters
  23039. an expression such as c1 + c2, it looks for one of the following two
  23040. functions:
  23041. operator+(const complex &, const complex &);
  23042. complex::operator+(const complex &);
  23043. The operator keyword is part of the function name. You could define a global
  23044. operator+ for adding two complex numbers like this:
  23045. complex operator+(const complex &c1, const complex &c2)
  23046. {
  23047. complex r;
  23048. r.real = c1.real + c2.real;
  23049. r.imag = c1.imag + c2.imag;
  23050. return r;
  23051. }
  23052. The compiler will not allow you to overload built-in operations, such as
  23053. addition of two ints, therefore at least one of the operands must be of a
  23054. user-defined type.
  23055. The IOStreams library uses operator overloading to determine how to format the
  23056. various built-in types. For example, the ostream class, of which cout is an
  23057. instance, overloads operator<< for all the built-in types. When the compiler
  23058. sees the expression
  23059. cout << i; // where i is an int
  23060. it generates the following function invocation
  23061. cout.operator<<(i);
  23062. which formats the number correctly.
  23063. Listing 10 shows how to extend IOStreams by overloading operator<< for complex
  23064. numbers (sample output in Listing 11). The compiler transforms the expression
  23065. cout << c // c is a complex number
  23066. into the function call
  23067. operator<<(cout, c)
  23068. which invokes operator<<(ostream&, const complex&). This function in turn
  23069. breaks the operation down into formatting objects of built-in types. This
  23070. function also returns the stream so that you can chain multiple stream
  23071. insertions in a single statement. For example, the expression
  23072. cout << c1 << c2
  23073. becomes
  23074. operator<<(operator<<(cout,c1), c2)
  23075.  
  23076. which requires that operator<<(ostream&, const complex&) return the stream.
  23077. The operator returns the stream by reference for efficiency.
  23078.  
  23079.  
  23080. Inline Functions
  23081.  
  23082.  
  23083. The inline keyword, seen in Listing 10, tells the compiler that you want the
  23084. code "inlined" for efficiency. Each call to an inline function inserts the
  23085. appropriate code inline, avoiding the usual overhead of an actual function
  23086. call. This mechanism is different from a function-like macro, which performs
  23087. text substitution before program translation. Inline functions have all the
  23088. type checking and semantics of true functions, without the sensitivity to side
  23089. effects that macros have. For example, you might define a macro to find the
  23090. smaller of two numbers as follows:
  23091. #define min(x,y) ((x) < (y) ? (x) : (y))
  23092. which fails miserably with an incremented argument, such as
  23093. min(x++,y++)
  23094. Inline functions don't have this problem, since they behave like real
  23095. functions.
  23096. Not all functions can or should be inlined, however. Certainly a recursive
  23097. function doesn't qualify for inlining. Large functions can increase code size
  23098. substantially when inlined. Inlining is mainly for small, simple functions.
  23099.  
  23100.  
  23101. Default Arguments
  23102.  
  23103.  
  23104. Default arguments allow a function to infer values from its prototype. The
  23105. program in Listing 12 has a function with the prototype:
  23106. int minutes(int hrs, int min = 0);
  23107. The "= 0" after the last parameter instructs the compiler to supply the value
  23108. 0 for the second argument when you omit it in a program. This mechanism is
  23109. essentially a shorthand for defining related overloaded functions. In this
  23110. case, it is a short cut for the following:
  23111. int minutes(int hrs, int min);
  23112. int minutes(int hrs); // ignores minutes
  23113. The complex constructor in Listing 10 uses default arguments to allow you to
  23114. define a complex number with 0, 1, or 2 arguments, as in
  23115. complex c1; // (0,0)
  23116. complex c2(1); // (1,0)
  23117. complex c3(2,3) // (2,3)
  23118. The third form is the one used in the return statement of operator+ in Listing
  23119. 10.
  23120.  
  23121.  
  23122. new and delete
  23123.  
  23124.  
  23125. To use the heap in C, you need to compute the size of the object you want to
  23126. create:
  23127. struct Foo *fp = malloc(sizeof(struct Foo));
  23128. In C++,the new operator computes the size of an object for you:
  23129. Foo *fp = new Foo;
  23130. To allocate an array in C, you call a different function:
  23131. struct Foo *fpa = calloc(n,sizeof(struct Foo));
  23132. In C++, new knows about arrays:
  23133. Foo *fpa = new Foo[n];
  23134. In addition, the new operator automatically invokes the appropriate
  23135. constructor to initialize the object(s) before it returns you the pointer. For
  23136. example, creating complex numbers on the heap automatically initializes them,
  23137. as in
  23138. complex *cp1 = new complex; // -> (0,0)
  23139. complex *cp2 = new complex(1); // -> (1,0)
  23140. complex *cp3 = new complex(2,3); // -> (2,3)
  23141. To return dynamic memory to the heap, you use the delete operator, which comes
  23142. in two forms. For singleton objects you use this one:
  23143. delete fp;
  23144. delete cp1;
  23145. but deleting arrays requires the second form, with the following syntax:
  23146. delete [] fpa; // array-delete syntax
  23147. Like other C++ features, new and delete improve the type safety of your
  23148. programs -- you aren't just asking for an amount of memory, you are requesting
  23149. objects, with the appropriate type-checking and initialization.
  23150.  
  23151.  
  23152. Declaration Statements
  23153.  
  23154.  
  23155. In C++, a declaration can appear anywhere a statement can. Instead of having
  23156. to group declarations at the beginning of a block, you can declare objects at
  23157. their point of first use. For example, in Listing 13 the array a is visible
  23158. throughout the function body, but n is not valid until its declaration, and i
  23159. not until the next line. With current compilers all three objects persist
  23160. until the end of the block, which is why i is not redeclared in the second
  23161. for-loop. The C++ standard now states, however, that the scope of variables
  23162. declared in a control loop is the loop itself. In the future, therefore, you
  23163. will have to redeclare i in the second loop, as in
  23164. for (int i = n-1; i >= 0; --i)
  23165.  
  23166.  
  23167.  
  23168. Static Initialization
  23169.  
  23170.  
  23171. In C, variables of static storage duration (those declared at file scope or
  23172. with the static keyword) are initialized at program startup and remain active
  23173. throughout program execution. Since such objects can only use constant
  23174. expressions as initializers, this isn't a challenge for compiler writers to
  23175. implement. Because objects in C++ usually require constructors, however,
  23176. static objects must be able to call functions in their initialization. For
  23177. this reason, C++ allows statements at file scope such as
  23178. double x = sqrt(5);
  23179.  
  23180.  
  23181. C Compatibility
  23182.  
  23183.  
  23184. To accommodate strong type-checking and object-orientation, C++ has had to
  23185. part ways with C on a few language issues. If you are going to use C++ as a
  23186. better C, you should be aware of those features that behave differently in the
  23187. two languages.
  23188. First of all, C++ has more keywords than C. You must avoid using any of the
  23189. tokens in Table 1 as identifiers in your programs.
  23190. You can use const integer objects and enumerated constants in array
  23191. declarations in C++, as in
  23192. const int SIZE = 100;
  23193. enum {BIGGER = 1000};
  23194. int a[SIZE], b[BIGGER];
  23195. Global const declarations have internal linkage by default, whereas in C they
  23196. have external linkage. As a result, you can use const definitions at file
  23197. scope in C++, in place of #define macros in header files. If you want a const
  23198. object to have external linkage, you must use the extern keyword.
  23199. In C, you can assign a pointer to any type to and from a void *. This allows
  23200. you to use malloc without a cast, as in
  23201. #include <stdlib.h>
  23202. ...
  23203. char *p = malloc(strlen*s) + 1);
  23204. The C++ type system will not allow you to assign from a void pointer without a
  23205. cast. For the example above, you should use the new operator anyway.
  23206. If you omit arguments in a function definition in C, the compiler does not
  23207. check how you use that function (i.e., you can pass any number and type of
  23208. arguments to it). In C++, the prototype f() is equivalent to f(void). If you
  23209. insist upon the unsafe C behavior, use f(...).
  23210. And finally, single-quoted character constants are of type char in C++, not
  23211. int. Otherwise, the expression
  23212. cout << 'a'
  23213. would print the internal character code (e.g., 97 for ASCII) instead of the
  23214. letter a.
  23215.  
  23216.  
  23217. Summary
  23218.  
  23219.  
  23220. You might think that the key distinction between C and C++ is support for
  23221. object-oriented programming, but even more crucial is the issue of type
  23222. safety. Not all programs are object-oriented programs. Even if you don't use
  23223. classes and other object-oriented mechanisms, using C++ as a type-safe C will
  23224. help make you a safer programmer.
  23225. Table 1 C++ Keywords and Reserved Words (as of November 1994)
  23226. and false signed
  23227. and_eq float sizeof
  23228. asm for static
  23229. auto friend static_cast
  23230. bitand goto struct
  23231. bitor if switch
  23232. bool inline template
  23233. break int this
  23234. case long throw
  23235. catch mutable true
  23236. char namespace try
  23237. class new typedef
  23238. compl not typeid
  23239. const_cast not_eq union
  23240. continue operator unsigned
  23241. default or using
  23242. delete or_eq virtual
  23243. do private void
  23244. double protected volatile
  23245. dynamic_cast public wchar_t
  23246. else register while
  23247. enum reinterpret_cast xor
  23248. explicit return xor_eq
  23249. extern short
  23250.  
  23251. Listing 1 Illustrates the need for function prototypes
  23252.  
  23253. /* convert1.c */
  23254. #include <stdio.h>
  23255.  
  23256. main()
  23257. {
  23258. dprint(123);
  23259. dprint(123.0);
  23260. return 0;
  23261. }
  23262.  
  23263. dprint(d)
  23264. double d;
  23265. {
  23266. printf("%f\n",d);
  23267. }
  23268.  
  23269. /* Output:
  23270. 0.000000
  23271. 123.000000
  23272. */
  23273.  
  23274. /* End of File */
  23275.  
  23276.  
  23277. Listing 2 Illustrates automatic conversion via function prototypes
  23278. /* convert2.c */
  23279. #include <stdio.h>
  23280.  
  23281. void dprint(double);
  23282.  
  23283. main()
  23284. {
  23285. dprint(123);
  23286. dprint(123.0);
  23287. return 0;
  23288. }
  23289.  
  23290. void dprint(double d)
  23291. {
  23292. printf("%f\n",d);
  23293. }
  23294.  
  23295. /* Output:
  23296. 123.000000
  23297. 123.000000
  23298. */
  23299.  
  23300. /* End of File */
  23301.  
  23302.  
  23303. Listing 3 Illustrates an implicit user-defined conversion
  23304. // convert3.cpp
  23305. #include <iostream. h>
  23306.  
  23307. struct A
  23308. {
  23309. double x;
  23310.  
  23311. A(double d)
  23312.  
  23313. {
  23314. cout << "A::A(double)" << endl;
  23315. x = d;
  23316. }
  23317. };
  23318.  
  23319. void f(const A& a)
  23320. {
  23321. cout << "f: "<< a.x << endl;
  23322. }
  23323.  
  23324. main()
  23325. {
  23326. A a(1);
  23327. f(a);
  23328. f(2);
  23329. return 0;
  23330. }
  23331.  
  23332. // Output:
  23333. A::A(double)
  23334. f: 1
  23335. A::A(double)
  23336. f:2
  23337.  
  23338. // End of File
  23339.  
  23340.  
  23341. Listing 4 Shows that only one user-defined conversion is allowed
  23342. // convert4.cpp
  23343. #include <iostream.h>
  23344.  
  23345. struct B;
  23346.  
  23347. struct A
  23348. {
  23349. double x;
  23350.  
  23351. A(const B& b);
  23352. };
  23353.  
  23354. void f(const A& a)
  23355. {
  23356. cout << "f: "<< a.x << endl;
  23357. }
  23358.  
  23359. struct B
  23360. {
  23361. double y;
  23362.  
  23363. B(double d) : y(d)
  23364. {
  23365. cout << "B::B(double)" << endl;
  23366. }
  23367. };
  23368.  
  23369. A::A(const B& b) : x(b.y)
  23370. {
  23371. cout << "A::A(const B&)" << endl;
  23372.  
  23373. }
  23374.  
  23375. main()
  23376. {
  23377. A a(1);
  23378. f(a);
  23379.  
  23380. B b(2);
  23381. f(b);
  23382.  
  23383. // The following won't compile:
  23384. // f(3)
  23385.  
  23386. // But these will:
  23387. f(B(3));
  23388. f(A(4));
  23389. return 0;
  23390. }
  23391.  
  23392. // Output:
  23393. B::B(double)
  23394. A::A(const B&)
  23395. f: 1
  23396. B::B(double)
  23397. A::A(const B&)
  23398. f: 2
  23399. B::B(double)
  23400. A::A(const B&)
  23401. f: 3
  23402. B::B(double)
  23403. A::A(const B&)
  23404. f: 4
  23405. // End of File
  23406.  
  23407.  
  23408. Listing 5 Illustrates program linkage (see also Listing 6)
  23409. void f(int);
  23410.  
  23411. main()
  23412. {
  23413. f(1);
  23414. return 0;
  23415. }
  23416.  
  23417. /* End of File */
  23418.  
  23419.  
  23420. Listing 6 A function intended to link with listing 5
  23421. #include <stdio.h>
  23422.  
  23423. void f(double x)
  23424. {
  23425. printf("f: %f\n",x);
  23426. }
  23427.  
  23428. /* End of File */
  23429.  
  23430.  
  23431. Listing 7 A swap function that illustrates call-by-reference
  23432.  
  23433. //swap.cpp
  23434. #include <stdio.h>
  23435.  
  23436. void swap(int &, int &);
  23437.  
  23438. main()
  23439. {
  23440. int i = 1, j = 2;
  23441.  
  23442. swap(i, j );
  23443. printf("i == %d, j == %d\n",i,j);
  23444. return 0;
  23445. }
  23446.  
  23447. void swap(int &x, int &y)
  23448. {
  23449. int temp = x;
  23450. x = y;
  23451. y = temp;
  23452. }
  23453.  
  23454. // Output:
  23455. i == 2, j == 1
  23456.  
  23457. // End of File
  23458.  
  23459.  
  23460. Listing 8 Returns an object from a function by reference
  23461. //retref.cpp: Returning a reference
  23462. #include <stdio.h>
  23463.  
  23464. int & current(); // Returns a reference
  23465.  
  23466. int a[4] = {0,1,2,3};
  23467. int index = 0;
  23468.  
  23469. main()
  23470. {
  23471. current() = 10;
  23472. index = 3;
  23473. current() = 20;
  23474. for (int i = 0; i < 4; ++i)
  23475. printf("%d ", a[i]);
  23476. putchar( '\n');
  23477. return 0;
  23478. }
  23479.  
  23480. int & current()
  23481. {
  23482. return a[index];
  23483. }
  23484.  
  23485. // Output:
  23486. 10 1 2 20
  23487.  
  23488. // End of File
  23489.  
  23490.  
  23491. Listing 9 A function template for swap()
  23492.  
  23493. //swap2.c
  23494. #include <iostream.h>
  23495. #include "complex.h"
  23496.  
  23497. template<class T>
  23498. void swap(T &x, T &y)
  23499. {
  23500. T temp = x;
  23501. x = y;
  23502. y = temp;
  23503. }
  23504.  
  23505. main()
  23506. {
  23507. int i = 1, j = 2;
  23508. swap(i,j);
  23509. cout << "i == " << i << ", j == " << j << endl;
  23510.  
  23511. double x = 3.0, y = 4.0;
  23512. swap(x,y);
  23513. cout << "x == " << x << ", y == " << y << endl;
  23514.  
  23515. char *s = "hi", *t = "there";
  23516. swap(s,t);
  23517. cout << "s == " << s << ", t == " << t << endl;
  23518.  
  23519. complex c1(5,6), c2(7,8);
  23520. swap(c1,c2);
  23521. cout << "c1 == " << c1 << ", c2 == " << c2 << edl;
  23522. return 0;
  23523. }
  23524.  
  23525. // Output:
  23526. i == 2, j == 1
  23527. x == 4, y == 3
  23528. s == there, t == hi
  23529. c1 == (7,8), c2 == (5,6)
  23530.  
  23531. /* End of File */
  23532.  
  23533.  
  23534. Listing 10 operator+ and operator<< for a complex number data type
  23535. #include <iostream.h>
  23536.  
  23537. struct complex
  23538. {
  23539. double real, imag;
  23540.  
  23541. complex(double = 0.0, double = 0.0);
  23542. };
  23543.  
  23544. complex::complex(double r, double i)
  23545. {
  23546. real = r;
  23547. imag = i;
  23548. }
  23549.  
  23550. inline ostream& operator<<(ostream &os, const complex &c)
  23551. {
  23552.  
  23553. os << '(' << c.real << ',' << c,imag << ')';
  23554. return os;
  23555. }
  23556.  
  23557. inline complex operator+(const complex &c1,
  23558. const complex &c2)
  23559. {
  23560. return complex(c1.real+c2.real,c1.imag+c2.imag);
  23561. }
  23562.  
  23563. /* End of File */
  23564.  
  23565.  
  23566. Listing 11 Uses the complex number data type
  23567. #include <iostream.h>
  23568. #include "complex.h"
  23569.  
  23570. main()
  23571. {
  23572. complex c1(1,2), c2(3,4);
  23573.  
  23574. cout << c1 << "+" << c2 <<" == "<< c1+c2 << endl;
  23575. return 0;
  23576. }
  23577.  
  23578. // Output:
  23579. (1,2) + (3,4) == (4,6)
  23580.  
  23581. /* End of File */
  23582.  
  23583.  
  23584. Listing 12 Illustrates default arguments
  23585. // minutes.cpp
  23586.  
  23587. #include <iostream.h>
  23588.  
  23589. inline int minutes(int hrs, int mins = 0)
  23590. {
  23591. return hrs * 60 + mins;
  23592. }
  23593.  
  23594. main()
  23595. {
  23596. cout << "3 hrs == " << minutes(3) <<" minutes" << endl;
  23597. cout << "3 hrs, 26 min ==" << minutes(3,26) <<" minutes" <<
  23598. endl;
  23599. return 0;
  23600. }
  23601.  
  23602. // Output:
  23603. 3 hrs == 180 minutes
  23604. 3 hrs, 26 min == 206 minutes
  23605.  
  23606. // End of File
  23607.  
  23608.  
  23609. Listing 13 Shows that declarations are statements
  23610. // declare.cpp
  23611. #include <iostream.h>
  23612.  
  23613.  
  23614. main()
  23615. {
  23616. int a[] = {0,1,2,3,4};
  23617.  
  23618. // Print address and size
  23619. cout << "a == " << (void *) a << endl;
  23620. cout << "sizeof(a) == "<< sizeof(a) << endl;
  23621.  
  23622. // Print forwards
  23623. size_t n = sizeof a / sizeof a[0];
  23624. for (int i = 0; i < n; ++i)
  23625. cout << a[i] << ' ';
  23626. cout << endl;
  23627.  
  23628. // Then backwards
  23629. for (i = n-1; i >= 0; --i)
  23630. cout << a[i] << ' ';
  23631. cout << endl;
  23632. return 0;
  23633. }
  23634.  
  23635. // Output:
  23636. a == 0xffec
  23637. sizeof(a) == 10
  23638. 0 1 2 3 4
  23639. 4 3 2 1 0
  23640.  
  23641. // End of File
  23642.  
  23643.  
  23644.  
  23645.  
  23646.  
  23647.  
  23648.  
  23649.  
  23650.  
  23651.  
  23652.  
  23653.  
  23654.  
  23655.  
  23656.  
  23657.  
  23658.  
  23659.  
  23660.  
  23661.  
  23662.  
  23663.  
  23664.  
  23665.  
  23666.  
  23667.  
  23668.  
  23669.  
  23670.  
  23671.  
  23672.  
  23673.  
  23674.  
  23675.  
  23676. Stepping Up To C++
  23677.  
  23678.  
  23679. Mutable Class Members
  23680.  
  23681.  
  23682.  
  23683.  
  23684. Dan Saks
  23685.  
  23686.  
  23687. Dan Saks is the president of Saks & Associates, which offers consulting and
  23688. training in C++ and C. He is secretary of the ANSI and ISO C++ committees. Dan
  23689. is coauthor of C++ Programming Guidelines, and codeveloper of the Plum Hall
  23690. Validation Suite for C++ (both with Thomas Plum). You can reach him at 393
  23691. Leander Dr., Springfield OH, 45504-4906, by phone at (513)324-3601, or
  23692. electronically at dsaks@wittenberg.edu.
  23693.  
  23694.  
  23695. For the past three months, I've been listing ways that the programming
  23696. language described by the C++ Draft Standard (as of Fall, 1994) differs from
  23697. the language described in the Annotated Reference Manual (ARM [1]). Thus far,
  23698. I've described the major extensions (see "Stepping Up to C++: C++ at CD
  23699. Registration," CUJ, January 1995.) I've also described numerous minor
  23700. enhancements (see "Stepping Up to C++: Minor Enhancements to C++ as of CD
  23701. Registration" and "...More Minor Enhancements to C++ as of CD Registration,"
  23702. CUJ, February and March, 1995.)
  23703. I have a few minor enhancements left to go. This month I present one more such
  23704. enhancement, mutable class members. Not that mutable members are very
  23705. important in themselves, but giving the background necessary to explain them
  23706. raises a lot of interesting design and language issues along the way, so I
  23707. think it's worth the time.
  23708. Mutable class members provide added support for logically-const class objects.
  23709. Understanding mutable class members requires an understanding of the const
  23710. qualifier, logical vs. physical const-ness, const member functions, and the
  23711. "casting away" of const. Those of you already familiar with these terms can
  23712. skip directly to the discussion of lazy evaluation, which presents a problem
  23713. solved by mutable class members. If you skip too far ahead, you can always
  23714. skip back.
  23715.  
  23716.  
  23717. The const Qualfier
  23718.  
  23719.  
  23720. C++ introduced the const qualifier as a way to specify read-only objects. When
  23721. applied to an object, const means the program may inspect but not change the
  23722. value of that object. A C++ translator is therefore free to place
  23723. const-qualified objects in read-only memory (ROM). In an environment that
  23724. supports multitasking with shared memory, a translator can place const objects
  23725. in a sharable, read-only data segment (which is just another flavor of ROM).
  23726. In some cases, a translator can even optimize a const object out of existence.
  23727. For example, given
  23728. const int MAX = 100;
  23729. a C++ translator (with the appropriate compiler and linker options) may place
  23730. MAX into ROM. Moreover, if the program never takes the address of, nor binds a
  23731. reference to MAX, a compiler need not generate storage for it. The compiler
  23732. can simply "optimize away" MAX by using its value (100) as an immediate
  23733. operand in generated machine instructions.
  23734. C++ erects various barriers to prevent programs from accidentally modifying
  23735. const objects. For example, a C++ compiler must diagnose every attempt to
  23736. modify a const-qualified object, such as
  23737. MAX = 99; // error
  23738. or
  23739. ++MAX; // error
  23740. Furthermore, the pointer and reference conversion rules won't let you strip
  23741. off const qualifiers (at least not without casts). For example,
  23742. int *pi = &MAX; // error
  23743. is an error because it attempts to convert a const int * (the type of &MAX) to
  23744. an int * (the type of pi), losing a const qualifier in the process. Failure to
  23745. catch this error would permit an otherwise valid assignment like
  23746. *pi = 99;
  23747. to write into a const object. Of course, you can strip a const qualifier using
  23748. a cast, such as
  23749. int *pi = (int *)&MAX;
  23750. or the new-style
  23751. int *pi = const_cast<int *>(&MAX);
  23752. if you think you know what you're doing. A cast expression that strips away a
  23753. const qualifier is said to "cast away const."
  23754. When applied to an aggregate, such as an array or struct, the const qualifier
  23755. actually percolates down to the elements of the aggregate. That is, the
  23756. elements of a const array are themselves const objects, as are the members of
  23757. a const object of a struct type. Thus, given
  23758. const char name[] = "jem";
  23759. an assignment such as
  23760. name[0] = "J";
  23761. is an error because it tries to alter the value of name[0], which is an object
  23762. of type const char.
  23763.  
  23764.  
  23765. Logically const vs. Physically const
  23766.  
  23767.  
  23768. C++ actually supports different degrees of const-ness. Some objects defined
  23769. const may be physically const, that is, residing in ROM and therefore
  23770. absolutely immutable. Other objects declared const are merely logically const.
  23771. A logically const object can't be modified in the current context, but it may
  23772. be modifiable in some other context.
  23773. For example, the standard header <string.h> declares the strlen function as
  23774. size_t strlen(const char *s);
  23775. Inside the body of strlen, *s designates a logically-const object of type
  23776. char. strlen can modify s, but it can't modify *s (any character addressed by
  23777. s). On some calls to strlen, s might point to an actual argument that's a
  23778. physically-const object. On other calls, it might point to a non-const object.
  23779. For example, during the call strlen(name), where name is as defined earlier, s
  23780. points to characters that are both logically and physically const. But during
  23781. the call strlen(title), where title is
  23782. char title[] = "Fing";
  23783. s is a pointer to logically-const characters that are actually non-const.
  23784. strlen can't change the characters in title, but other parts of the program
  23785. might.
  23786. In general, I recommend using const as often as appropriate in declarations.
  23787. Specifically, I suggest declaring every logically- or physically-const object
  23788. as such by using the const qualifier explicitly. Any purportedly
  23789. general-purpose library must obey this rule, or it won't be very useful in
  23790. ROM-based applications.
  23791. For instance, the standard strcmp function is declared
  23792.  
  23793. int strcmp
  23794. (const char *s1, const char *s2);
  23795. strcmp compares two strings without altering them. Therefore, inside strcmp,
  23796. both s1 and s2 point to logically-const strings, and the declaration says so
  23797. explicitly. If either const were missing, an application could not use this
  23798. function to compare two const strings. On the other hand, a function such as
  23799. char *strcpy(char *s1, const char *s2);
  23800. can only declare its second parameter as const. strcpy must reserve the right
  23801. to alter the string addressed by s1, otherwise it can't do its job of copying
  23802. s2 to s1.
  23803. Even if you don't write ROM-based applications, you should still use the const
  23804. qualifier generously. Using the const qualifier builds more static type
  23805. information into programs. The compiler can use this information to detect
  23806. logic errors. As always, the more you tell the compiler about your intent, the
  23807. more easily it can tell when your program violates that intent. Code that
  23808. consistently enforces logical constness without casting away const is said to
  23809. be "const correct."
  23810.  
  23811.  
  23812. const Member Functions
  23813.  
  23814.  
  23815. Indeed, writing const-correct classes requires extra effort. For instance,
  23816. consider the array class template sketched in Listing 1. (This is a
  23817. templatized version of the float_array class I used for my examples in
  23818. "Stepping Up to C++: Dynamic Arrays," CUJ, November, 1992.) Let's see what's
  23819. required to make it const correct.
  23820. Listing 1 includes a non-member function template, sigma, that returns the sum
  23821. of the elements in an array<T>. The function declaration:
  23822. template <class T>
  23823. T sigma(array<T> &a)
  23824. suggests by the absence of any const qualifiers that sigma might change its
  23825. actual argument in the course of summing the elements. But logically, sigma
  23826. shouldn't change its argument, and nothing in the function body seems to
  23827. suggest that it does. Therefore, sigma's parameter should be const, as in
  23828. template <class T>
  23829. T sigma(const array<T> &a)
  23830. so the compiler can enforce the logical const-ness of array a.
  23831. In effect, the const qualifier in the declaration is sigma's promise that it
  23832. won't change the actual array<T> object referenced by a. The compiler then
  23833. backs that promise by rejecting any statement in sigma's body that tries to
  23834. change a. In particular, since every member of a const object is itself const,
  23835. the compiler rejects any statement that tries to change a member of a. This
  23836. includes any attempt to pass a, or a member of a, as a non-const argument to
  23837. another function.
  23838. Recompiling the code after adding the const qualifier to sigma's parameter
  23839. list triggers a number of compile-time errors inside sigma. For one, the
  23840. compiler complains about the call to a.length() in
  23841. for (size_t i = 0; i < a.length(); ++i)
  23842. sum += a[i];
  23843. The problem is, now that a is const, sigma can't call a member function
  23844. applied to a unless it can be sure that the member function won't alter a. How
  23845. can you tell the compiler that calling a. length() won't alter a? By declaring
  23846. length as a const member function:
  23847. size_t length() const;
  23848. In effect, a const member function promises to treat the object to which it
  23849. applies as a const object. Once you change length to a const member function,
  23850. sigma has no problem calling it.
  23851. The keyword const after the parameter list actually modifies the object
  23852. addressed by the implicit this parameter. A non-const member function for a
  23853. class X implicity declares this as
  23854. X *const this;
  23855. That is, this is a non-modifiable pointer to a modifiable object. In a const
  23856. member function, the implicit declaration for this is:
  23857. const X *const this;
  23858. or equivalently:
  23859. X const *const this;
  23860. In other words, this is a non-modifiable pointer to a non-modifiable object.
  23861. You can apply a const member function to a non-const object. The function
  23862. simply treats the non-const object as const. However, you cannot apply a
  23863. non-const member function to a const object, because the non-const member
  23864. function might try to change the const object.
  23865. Adding the const qualifier to sigma's parameter list also causes a
  23866. compile-time error on the call a[i] inside sigma. Again, the problem is that
  23867. array<T>::operator[](size_t) is a non-const member function. Rather than
  23868. simply change operator[] to a const member function, a better solution is to
  23869. overload operator[] as both const and non-const member functions:
  23870. const T &operator[](size_t i) const;
  23871. T &operator[](size_t i);
  23872. Hence, the expression a[i] invokes the const operator[] if a is a const array,
  23873. and invokes the non-const operator[] if a is non-const. Both forms of
  23874. operator[] return a reference to the selected array element, but the const
  23875. form returns a reference to a const array element, while the non-const form
  23876. returns a reference to a non-const element. The const-corrected array class
  23877. appears in Listing 2. (For more about overloading operator[] as both const and
  23878. non-const member functions, see "Stepping Up to C++: operator[]," CUJ,
  23879. January, 1993, or Meyers [2].)
  23880.  
  23881.  
  23882. Lazy Evaluation
  23883.  
  23884.  
  23885. Without a doubt, the primary benefit of C++ classes is that they support data
  23886. abstraction. A well-written class defines an abstract type with complete and
  23887. consistent behavior that you can use with little or no concern for its
  23888. underlying data representation. It hides the data representation as private
  23889. data members, and grants restricted access to that data through public member
  23890. functions.
  23891. Most classes have attributes that you can represent in more than one way. For
  23892. a particular class attribute, you might simply store a value representing the
  23893. attribute in a data member. Or, you might be able to compute the attribute
  23894. from other data in the object's representation.
  23895. For example, complex numbers have two common representations:
  23896. 1. in rectangular form, (r, i), where r is the real part and i is the
  23897. imaginary part
  23898. 2. in polar form, (rho, theta), where rho is the magnitude (distance from the
  23899. origin), and theta is the phase angle (typically in radians)
  23900. These two forms are related by the following equations:
  23901. rho = sqrt(re * re + im * im);
  23902. theta = arctan(im/re);
  23903. If your application for complex numbers uses both forms at one time or
  23904. another, you might design a complex number class that stores both
  23905. representations:
  23906. class complex
  23907. {
  23908. public:
  23909. // ...
  23910. private:
  23911. double re, im;
  23912. double rho, theta;
  23913.  
  23914. };
  23915. This design is certainly straightforward, but it has at least two drawbacks:
  23916. 1. It doubles the storage requirements for every complex number.
  23917. 2. Every arithmetic operation on complex numbers, such as + or *, must compute
  23918. the results twice -- once for each form.
  23919. As an alternative, your complex class can store just one form, and compute the
  23920. other form on demand, as shown in Listing 3. The complex class in Listing 3
  23921. stores only the rectangular form, and recomputes the polar form on demand. For
  23922. instance, calling z1. real() simply returns the value of the private data
  23923. member re, which stores the real part. Calling z1.theta() computes the angle
  23924. using re and im, the imaginary part.
  23925. Note that the four member functions, real, imag, rho, and theta, are const
  23926. member functions. And well they should be. Indeed, none of them changes the
  23927. value of the complex number. I see no reason to prevent a program from
  23928. requesting these values of a const complex object.
  23929. The problem with computing the polar form on demand is that some applications
  23930. might calculate the rho and theta for the same complex numbers over and over
  23931. again. Granted, the computation is not that complicated, but it uses the sqrt
  23932. and atan2 functions which are more than just a few instructions on most
  23933. architectures. If these recalculations prove to be too expensive, a different
  23934. design for complex numbers might be appropriate.
  23935. Listing 4 shows a complex number class that caches the polar form in a
  23936. dynamically-allocated auxiliary structure. Each complex number has three
  23937. private data members:
  23938. class complex
  23939. {
  23940. //...
  23941. private:
  23942. double re, im;
  23943. struct polar;
  23944. polar *p;
  23945. };
  23946. As before, re and im hold the real and imaginary parts. p is a a pointer to an
  23947. auxiliary structure of type polar. polar is a forward-declared nested type,
  23948. defined simply as
  23949. struct complex::polar
  23950. {
  23951. double rho, theta;
  23952. };
  23953. That is, it holds the polar representation of a complex number.
  23954. By this design, all complex numbers start out by storing only the rectangular
  23955. form (in re and im). The constructors always store 0 (a null pointer) into
  23956. member p. Most arithmetic operations, such as operator+ in Listing 4, use the
  23957. rectangular forms of the operands, and return a result in rectangular form.
  23958. Since operator+ uses a constructor to build the result, that constructor sets
  23959. the pointer in the result to null.
  23960. Like its previous version (in Listing 3), this complex class never computes
  23961. the polar form until needed. However, unlike before, this implementation
  23962. doesn't just discard the result and calculate again when asked. Rather, it
  23963. dynamically allocates a complex::polar object, and caches the result in that
  23964. object. If the value of the complex object doesn't change, the class satisfies
  23965. repeated requests for the polar coordinates by reading them from the cache,
  23966. which is much faster than recalculating.
  23967. This caching technique is an example of a more general technique known in some
  23968. circles as "lazy evaluation." The basic philosophy of lazy evaluation is
  23969. "Don't do it unless you have to, and then don't do it again." This technique
  23970. is most useful for any class that has an attribute where
  23971. accessing the attribute's value incurs a high cost in time or space or both,
  23972. and
  23973. typical applications request the attribute's value from relatively few
  23974. objects.
  23975. Meyers [2], Murray [3], and Plum and Saks [4] offer other examples using lazy
  23976. evaluation (although none uses that term). Meyers uses lazy evaluation to
  23977. postpone computing the length of string objects. Murray presents a variation
  23978. on the complex number class. Plum and I sketch a class for tokens (such as in
  23979. a parser) that caches each token's hash value during symbol lookup.
  23980.  
  23981.  
  23982. Casting Away const, Again
  23983.  
  23984.  
  23985. As in the earlier complex class (Listing 3), the four member functions, real,
  23986. imag, rho, and theta of the later class (Listing 4) are also const member
  23987. functions. Programmers using complex numbers should quite reasonably expect to
  23988. be able to obtain any of these values from a const complex object, regardless
  23989. of the implementation. There's no problem declaring real and imag in Listing 4
  23990. as const; they are identical to the corresponding functions in Listing 3.
  23991. However, the rho and theta functions in Listing 4 are a little tricky and
  23992. require a careful look.
  23993. The problem with the rho and theta functions is that even though they don't
  23994. change the value of a complex object as seen outside the class, they might
  23995. change the private data of that object. Specifically, if pointer p is null,
  23996. then rho or theta must change p to point to a newly-allocated polar object,
  23997. using something of the form:
  23998. if (p == 0)
  23999. p = new polar(..., ...);
  24000. However, in a const member function of class complex, this points to a const
  24001. complex object, meaning that re, im and p are themselves const. Thus, the
  24002. assignment to p as written above is an error.
  24003. Of course, you can always change rho and theta to non-const member functions.
  24004. But then you won't be able to call rho and theta for a const complex object.
  24005. Not good. Alternatively, you can leave rho and theta as const member functions
  24006. and cast away const from the complex object inside each function, as shown in
  24007. Listing 4.
  24008. There are various ways to cast away const inside a const member function. rho
  24009. and theta each illustrate a different style. rho uses the following approach:
  24010. complex *This =
  24011. const_cast<complex *>(this);
  24012. This->p = new polar(..., ...);
  24013. This is local variable which points to the same object as this. However, This
  24014. is declared without const, so rho can use it to treat the object as non-const.
  24015. Copying this to This loses a const qualifer, so the conversion requires a cast
  24016. to sneak past the compiler. I used the new-style cast. On compilers that don't
  24017. yet support new-style casts, use
  24018. complex *This = (complex *)this;
  24019. theta casts away const a little differently. It simply applies a cast to p
  24020. itself:
  24021. (polar *&)p = new polar(..., ..);
  24022. This casts p to a reference to a (non-const) pointer to polar. Writing the
  24023. cast as just (polar *)p converts p to the correct type, but the cast yields an
  24024. rvalue. An rvalue expression cannot appear on the left of an assignment.
  24025. Casting to a reference yields an lvalue, which can appear to the left side of
  24026. an assignment. The reference yields the same result as the slightly
  24027. longer-winded pointer cast, *(polar **)&p.
  24028. By the way, Listing 4 uses only two relatively new C++ features: the
  24029. forward-declaration of nested class complex::polar and the new-style cast in
  24030. complex::rho. I found two compilers that will compile and execute Listing 4 as
  24031. written, namely Borland C++ 4.5 for DOS/Windows, and MetaWare High C++ 3.3 for
  24032. Extended DOS using the Phar Lap 386 DOS Extender 4.1. If you move the
  24033. definition for complex::polar back inside the definition for complex, and
  24034. change the new-style cast to an old-style cast, most other compilers should
  24035. accept it.
  24036.  
  24037.  
  24038. At last, Mutable Class Members
  24039.  
  24040.  
  24041. If using casts, as I did above, makes you queasy, that's good. Casts can be
  24042. pretty dangerous, and you should use them sparingly and with great care. In
  24043. this case, how can you know that it's safe to cast away const from a complex
  24044. number? Functions such as rho and theta are indeed logically const, but they
  24045. assume the object is not physically const (it's not in ROM). If the complex
  24046. object is really physically const, writing into the object causes undefined
  24047. behavior.
  24048. Mutable class members offer a way to implement logically const operations
  24049. without casting away const. A mutable member is one that is never const, even
  24050. if it is a member of an object declared const. For example, in
  24051. class complex
  24052. {
  24053. public:
  24054.  
  24055. // ....
  24056. private:
  24057. double re, im;
  24058. struct polar;
  24059. mutable polar *p;
  24060. };
  24061. Here, p is always a non-const object, even in complex objects declared const.
  24062. Thus, const member functions like rho and theta need not cast away const in
  24063. order to write to p. Listing 5 shows the changes to the complex class that
  24064. result from using a mutable member instead of casts. So far, I've found only
  24065. one compiler that compiles and executes this code -- the MetaWare/Phar Lap
  24066. combination.
  24067. Syntactically, mutable is a storage class specifier, like auto, register, or
  24068. static. It can only appear in declarations of class data members. You cannot
  24069. declare a data member both mutable and const. For example,
  24070. class X
  24071. {
  24072. mutable const int *p; // ok
  24073. mutable int *const q; // error
  24074. };
  24075. The declaration for p is okay because (ignoring the mutable specifier for the
  24076. moment) p is a non-const pointer to a const int. Adding mutable to the
  24077. declaration specifies that p will always be non-const, even in a const X
  24078. object.
  24079. The declaration for q is an error because (ignoring the mutable specifier for
  24080. the moment) q is a const pointer to a non-const int. That is, q itself is
  24081. declared const. Adding the mutable specifier to the declaration conflicts with
  24082. the const already applied to q, hence the error.
  24083. Next month... the remaining minor enhancements.
  24084. References
  24085. [1] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference
  24086. Manual. (Addison-Wesley, 1990).
  24087. [2] Scott Meyers. Effective C++. (Addison-Wesley, 1992).
  24088. [3] Robert B. Murray. C++ Strategies and Tactics. (Addison-Wesley, 1993).
  24089. [4] Thomas Plum and Dan Saks. C++ Programming Guidelines. (Plum Hall, 1991).
  24090.  
  24091. Listing 1 A simple array class template
  24092. // array1.cpp
  24093.  
  24094. #include <stddef.h>
  24095.  
  24096. template <class T>
  24097. class array
  24098. {
  24099. public:
  24100. array(size_t n = 0);
  24101. array(const array &a);
  24102. ~array();
  24103. array &operator=(const array &a);
  24104. T &operator[](size_t i);
  24105. size_t length();
  24106. private:
  24107. T *pa;
  24108. size_t len;
  24109. };
  24110.  
  24111. // ...
  24112.  
  24113. template <class T>
  24114. T &array<T>::operator[](size_t i)
  24115. {
  24116. return pa[i];
  24117. }
  24118.  
  24119. template <class T>
  24120. inline size_t array<T>::length()
  24121. {
  24122. return len;
  24123. }
  24124.  
  24125. ...
  24126.  
  24127. template <class T>
  24128.  
  24129. T sigma(array<T> &a)
  24130. {
  24131. T sum = 0;
  24132. for (size_t i = 0; i < a.length(); ++i)
  24133. sum += a[i];
  24134. return sum;
  24135. }
  24136. // End of File
  24137.  
  24138.  
  24139. Listing 2 A simple const-correct array class template
  24140. // array2.cpp
  24141.  
  24142. #include <stddef.h>
  24143.  
  24144. template <class T>
  24145. class array
  24146. {
  24147. public:
  24148. array(size_t n = 0);
  24149. array(const array &a);
  24150. ~array();
  24151. array &operator=(const array &a);
  24152. const T &operator[](size_t i) const;
  24153. T &operator[](size_t i);
  24154. size_t length() const;
  24155. private:
  24156. T *pa;
  24157. size_t len;
  24158. };
  24159.  
  24160. // ...
  24161.  
  24162. template <class T>
  24163. const T &array<T>::operator[](size_t i) const
  24164. {
  24165. return pa[i];
  24166. }
  24167.  
  24168. template <class T>
  24169. T &array<T>::operator[](size_t i)
  24170. {
  24171. return pa[i];
  24172. }
  24173.  
  24174. template <class T>
  24175. inline size_t array<T>::length() const
  24176. {
  24177. return len;
  24178. }
  24179.  
  24180. ...
  24181.  
  24182. template <class T>
  24183. T sigma(const array<T> &a)
  24184. {
  24185. T sum = 0;
  24186. for (size_t i = 0; i < a.length(); ++i)
  24187. sum += a[i];
  24188.  
  24189. return sum;
  24190. }
  24191. // End of File
  24192.  
  24193.  
  24194. Listing 3 A rudimentary class for complex numbers which recomputes the polar
  24195. form on demand
  24196. // z1.cpp
  24197.  
  24198. #include <iostream.h>
  24199. #include <iomanip.h>
  24200. #include <math.h>
  24201.  
  24202. class complex
  24203. {
  24204. public:
  24205. complex(double r, double i);
  24206. double real() const;
  24207. double imag() const;
  24208. double rho() const;
  24209. double theta() const;
  24210. private:
  24211. double re, im;
  24212. };
  24213.  
  24214. inline complex::complex(double r, double i)
  24215. : re(r), im(i)
  24216. {
  24217. }
  24218.  
  24219. inline double complex::real() const
  24220. {
  24221. return re;
  24222. }
  24223.  
  24224. inline double complex::imag() const
  24225. {
  24226. return im;
  24227. }
  24228.  
  24229. inline double complex::rho() const
  24230. {
  24231. return sqrt(re * re + im * im);
  24232. }
  24233.  
  24234. inline double complex::theta() const
  24235. {
  24236. return atan2(im, re);
  24237. }
  24238.  
  24239. complex operator+(const complex &z1, const complex &z2)
  24240. {
  24241. return complex
  24242. (z1.real() + z2.real(), z1.imag() + z2.imag());
  24243. }
  24244.  
  24245. int main()
  24246. {
  24247. complex z1(3, 4);
  24248. cout << '(' << z1.real() << ',' << z1.imag() << ')'
  24249.  
  24250. << endl;
  24251. cout << '(' << z1.rho() << ',' << z1.theta() << ')'
  24252. << endl;
  24253. complex z2(1, 1);
  24254. cout << '(' << z2.real() << ',' << z2.imag() << ')'
  24255. << endl;
  24256. cout << '(' << z2.rho() << ',' << z2.theta() << ')'
  24257. << endl;
  24258. z1 = z1 + z2;
  24259. cout << '(' << z1.real() << ',' << z1.imag() << ')'
  24260. << endl;
  24261. cout << '(' << z1.rho() << ',' << z1.theta() << ')'
  24262. << endl;
  24263. return 0;
  24264. }
  24265. // End of File
  24266.  
  24267.  
  24268. Listing 4 A rudimentary class for complex numbers using "lazy" evaluation and
  24269. caching for polar form
  24270. // z2.cpp
  24271.  
  24272. #include <iostream.h>
  24273. #include <iomanip.h>
  24274. #include <math.h>
  24275.  
  24276. class complex
  24277. {
  24278. public:
  24279. complex(double r, double i);
  24280. complex(const complex &z);
  24281. complex &operator=(const complex &z);
  24282. ~complex();
  24283. double real() const;
  24284. double imag() const;
  24285. double rho() const;
  24286. double theta() const;
  24287. private:
  24288. double re, im;
  24289. struct polar;
  24290. polar *p;
  24291. };
  24292.  
  24293. struct complex::polar
  24294. {
  24295. polar(double r, double t);
  24296. double rho, theta;
  24297. };
  24298.  
  24299. inline complex::polar::polar(double r, double t)
  24300. : rho(r), theta(t)
  24301. {
  24302. }
  24303.  
  24304. inline complex::complex(double r, double i)
  24305. : re(r), im(i), p(0)
  24306. {
  24307. }
  24308.  
  24309. inline complex::complex(const complex &z)
  24310.  
  24311. : re(z.re), im(z.im), p(0)
  24312. {
  24313. }
  24314.  
  24315. inline complex &complex::operator=(const complex &z)
  24316. {
  24317. re = z.re;
  24318. im = z.im;
  24319. p = 0;
  24320. return *this;
  24321. }
  24322.  
  24323. inline complex::~complex()
  24324. {
  24325. delete p;
  24326. }
  24327.  
  24328. inline double complex::real() const
  24329. {
  24330. return re;
  24331. }
  24332.  
  24333. inline double complex::imag() const
  24334. {
  24335. return im;
  24336. }
  24337.  
  24338. double complex::rho() const
  24339. {
  24340. if (p == 0)
  24341. {
  24342. complex *This = const_cast<complex *>(this);
  24343. This->p =
  24344. new polar(sqrt(re*re + im*im), atan2(im, re));
  24345. }
  24346. return p->rho;
  24347. }
  24348.  
  24349. double complex::theta() const
  24350. {
  24351. if (p == 0)
  24352. (polar *&)p =
  24353. new polar(sqrt(re*re + im*im), atan2(im, re));
  24354. return p->theta;
  24355. }
  24356.  
  24357. complex operator+(const complex &z1, const complex &z2)
  24358. {
  24359. return complex
  24360. (z1.real() + z2.real(), z1.imag() + z2.imag());
  24361. }
  24362.  
  24363. int main()
  24364. {
  24365. // same as Listing 3
  24366. }
  24367. // End of File
  24368.  
  24369.  
  24370.  
  24371. Listing 5 A rudimentary class for complex numbers using a mutable member to
  24372. implement "lazy" evaluation and caching for polar form
  24373. // z3.cpp
  24374.  
  24375. #include <iostream.h>
  24376. #include <iomanip.h>
  24377. #include <math.h>
  24378.  
  24379. class complex
  24380. {
  24381. public:
  24382. complex(double r, double i);
  24383. complex(const complex &z);
  24384. complex &operator=(const complex &z);
  24385. ~complex();
  24386. double real() const;
  24387. double imag() const;
  24388. double rho() const;
  24389. double theta() const;
  24390. private:
  24391. double re, im;
  24392. struct polar;
  24393. mutable polar *p;
  24394. };
  24395.  
  24396. // ... same as Listing 4 ...
  24397.  
  24398. double complex::rho() const
  24399. {
  24400. if (p == 0)
  24401. p = new polar(sqrt(re*re + im*im), atan2(im, re));
  24402. return p->rho;
  24403. }
  24404.  
  24405. double complex::theta() const
  24406. {
  24407. if (p == 0)
  24408. p = new polar(sqrt(re*re + im*im), atan2(im, re));
  24409. return p->theta;
  24410. }
  24411.  
  24412. complex operator+(const complex &z1, const complex &z2)
  24413. {
  24414. return complex
  24415. (z1.real() + z2.real(), z1.imag() + z2.imag());
  24416. }
  24417.  
  24418. int main()
  24419. {
  24420. // same as Listing 3 and Listing 4
  24421. }
  24422. // End of File
  24423.  
  24424.  
  24425.  
  24426.  
  24427.  
  24428.  
  24429.  
  24430.  
  24431.  
  24432.  
  24433.  
  24434.  
  24435.  
  24436.  
  24437.  
  24438.  
  24439.  
  24440.  
  24441.  
  24442.  
  24443.  
  24444.  
  24445.  
  24446.  
  24447.  
  24448.  
  24449.  
  24450.  
  24451.  
  24452.  
  24453.  
  24454.  
  24455.  
  24456.  
  24457.  
  24458.  
  24459.  
  24460.  
  24461.  
  24462.  
  24463.  
  24464.  
  24465.  
  24466.  
  24467.  
  24468.  
  24469.  
  24470.  
  24471.  
  24472.  
  24473.  
  24474.  
  24475.  
  24476.  
  24477.  
  24478.  
  24479.  
  24480.  
  24481.  
  24482.  
  24483.  
  24484.  
  24485.  
  24486.  
  24487.  
  24488.  
  24489.  
  24490.  
  24491.  
  24492.  
  24493.  
  24494.  
  24495. On the Networks
  24496.  
  24497.  
  24498. Relate Your Way Through the Storm
  24499.  
  24500.  
  24501.  
  24502.  
  24503. Sydney S.Weinstein
  24504.  
  24505.  
  24506. Sydney S. Weinstein, CDP, CCP is a consultant, columnist, lecturer, author,
  24507. professor, and President of Myxa Corporation, an Open Systems Technology
  24508. company specializing in helping companies move to and work with Open Systems.
  24509. He can be contacted care of Myxa Corporation, 3837 Byron Road, Huntingdon
  24510. Valley, PA 19006-2320, or via electronic mail using the lnternet/USENET
  24511. mailbox syd@Myxa .com. (dsinc!syd for those that cannot do Internet
  24512. addressing).
  24513.  
  24514.  
  24515. One of the highlights the past few months has been typhoon from Thomas B.
  24516. Pedersen <zeppelin@login.dknet.dk>. It's a complete, freely available
  24517. relational database management system for the UNIX and OS/2 environments. He
  24518. contributed it for posting in comp.sources.misc as Volume 44, Issues 57-65.
  24519. typhoon was originally inspired by Raima's db_VISTA (today Raima Data Manager)
  24520. but is relational rather than network based. typhoon lacks some of db_VISTA's
  24521. features, but also contains a number of nice features not found in db_VISTA.
  24522. All relations are defined in a Data Definition Language (dd1) file. You define
  24523. the database relations like you would write a C structure with chars, ints,
  24524. strings, multi-dimensional arrays, nested union, and structures, etc. Then you
  24525. define primary, alternate, and foreign keys for each relation. The Data
  24526. Definition Language Processor (ddlp) compiles the database definition into a
  24527. binary file which constitutes the database description. The database relations
  24528. are accessed via C subroutines which manipulate individual records within a
  24529. table. typhoon supports the following features:
  24530. multiple open databases
  24531. multi-field keys
  24532. nested structures in records
  24533. controlled unions
  24534. referential integrity
  24535. variable-length fields
  24536. null keys (optional keys in db_VISTA, but easier to use than db_VISTA's)
  24537. dynamic opening and closing of database files
  24538. At present the database has no locking mechanism but it's in the works.
  24539. Currently the author is working on extending the library to support locking,
  24540. logging, transactions, and paging (to improve performance).
  24541. The library comes with three additional utilities:
  24542. dbdview displays the database definitions
  24543. tyexport exports tables from a database
  24544. tyimport imports tables into a database
  24545. Additional tools awaiting release, but being held up due to documentation
  24546. issues, include online backup, restore, and a replication server.
  24547. Continuing on with highlights from comp.sources.misc, I report that an
  24548. extension to Perl 4 to allow access to Sybase databases was re-released at
  24549. version 1.011 as sybperl in Volume 43, Issues 46-47 by Michael Peppler
  24550. <mpeppler@itf.ch>. Features added since the last release (1.009) include
  24551. dbfreebuf, dbsetopt, support to set the language and character set on the fly
  24552. via DBSETLCHARSET and DBSETLNATLANG, access to non-ASCII text, extensions to
  24553. eg/dbschema.pl to extract stored procedures and views as well as tables, casts
  24554. of data types now performed with DBlibrary typedefs instead of C datatypes,
  24555. and some bug fixes. Note: This package does not work with the new Perl 5
  24556. version, just with the older Perl 4 versions.
  24557. Brad Eacker <beacker@sgi.com> contributed a set of tools and library routines
  24558. to manipulate xbase files for Volume 43, Issues 48-49. dbf includes tools to
  24559. list all the records in the file, extract a specific record, add or mark as
  24560. deleted a specific record and to removed marked deleted records from the file.
  24561. It also includes a utility to create the template file and a basic grammar
  24562. recognizer for xbase .prg files.
  24563. A C++ class library to perform generic processing of gray-scale images was
  24564. contributed for Volume 43, Issues 51-55 by Kiselyov Oleg
  24565. <oleg@ponder.csci.unt.edu>. grayimage works on both images or rectangular
  24566. areas within images, and can perform addition, scalar product, and
  24567. modification of pixel values, based on equalization, histogram and
  24568. filtration/convolution. The library implements morphological filtration as
  24569. well. The package can read/write XWD, Group G TIFF, and PGM file formats
  24570. selecting the appropriate method automatically.
  24571. The latest version of zsh, an extended UNIX shell based on the Bourne shell
  24572. syntax, was released for Volume 43, Issues 89-107 by Bas de Bakker
  24573. <zsh-list@sterling.com>. zsh adds lots of features to the standard Bourne
  24574. shell syntax, including command-line editing, complete with programmable
  24575. command and file completion; multi-line commands; variable editing; command
  24576. buffer stack; printing text into the editing buffer; menu completion and
  24577. inline expansion of variables; and history commands. zsh also adds an enhanced
  24578. globbing package for wildcard expansion, including recursive globbing (like
  24579. the UNIX find command) and file attribute qualifiers. It supports an enhanced
  24580. redirection syntax including tee-like features, named directories, internal
  24581. integer arithmetic commands, manipulation of array variables, and even
  24582. spelling correction. zsh runs on almost any UNIX system.
  24583. The latest version of Project Info-ZIP's <zip-bugs@wkuvx1.wku.edu> portable
  24584. UnZip utility was released as Volume 44, Issues 66-85. Version 5.12, is the
  24585. first post of the 55.1 version and includes a self extraction stub, a rewrite
  24586. of unshrink to avoid copyfight problems, -C option for case-insensitive
  24587. filename matching, an added -L option to auto-convert filename case from upper
  24588. to lower, wildcard support in UnZip itself, extraction to a specified
  24589. directory other than the current directory, and performance tuning and bug
  24590. fixes. While compatible with PKUNZIP, UnZip supports UNIX (many flavors), VMS,
  24591. OS/2, MSDOS (+ Windows), NT, TOPS-20 (partly), AmigaDOS, Atari TOS, Macintosh,
  24592. and Human68k. UnZip features not found in PKUNZIP include source code; default
  24593. extraction of directory trees (with a switch to defeat this, rather than the
  24594. reverse); VMS, Macintosh, and OS/2 extended file attributes; and, of course,
  24595. the ability to run under most of your favorite operating systems.
  24596. The latest version of the JPEG image compression software was contributed by
  24597. the Independent JPEG Group <jpeg-info@uunet.uu.net> for Volume 44 Issues
  24598. 98-124. This package contains C software to implement JPEG image compression
  24599. and decompression. JPEG is a standardized compression method for full-color
  24600. and gray-scale images. JPEG is intended for real-world scenes; cartoons and
  24601. other non-realistic images are not its strong suit. JPEG is lossy, so the
  24602. output image is not identical to the input image. The user can trade off
  24603. output image quality against compressed file size by adjusting a compression
  24604. parameter.
  24605. Version 5 of the jpeg system includes a reusable JPEG
  24606. compression/decompression library, plus sample applications cjpeg and djpeg,
  24607. which perform conversion between JPEG JFIF format and image files in PPM/PGM
  24608. (PBMPLUS), GIF, BMP, Utah RLE, and Targa formats. Two small applications,
  24609. wrjpgcom and rdjpgcom, insert and extract textual comments in JFIF files. The
  24610. package is highly portable; it has been used successfully on many machines
  24611. ranging from Apple IIs to Crays. Version 5 includes user-level improvements
  24612. over version 4:
  24613. Automatic configuration, which simplifies installation for most UNIX systems
  24614. A range of speed vs. image quality tradeoffs including image resizing during
  24615. decompression (scaling down by a factor of 1/2, 1/4, or 1/8 is handled very
  24616. efficiently)
  24617. New programs rdjpgcom and wrjpgcom that allow insertion and extraction of text
  24618. comments in a JPEG file
  24619. BMP file format support within programs cjpeg/djpeg
  24620. The application programmer's interface to the library has changed completely.
  24621. Some of the most notable improvements for the programmer are as follows:
  24622. The need for callback routines for handling the uncompressed image data has
  24623. been eliminated. The application now sees the library as a set of routines
  24624. that it calls to read or write image data on a scanline-by-scanline basis.
  24625. The application image data is represented in a conventional interleaved-pixel
  24626. format, rather than as a separate array for each color channel. This form of
  24627. representation can save a copying step in many programs.
  24628. The handling of compressed data has been cleaned up: the application can
  24629. supply routines to source or sink the compressed data.
  24630. All static state data has been eliminated from the library, so that multiple
  24631. instances of compression or decompression can be active concurrently.
  24632. JPEG-abbreviated datastream formats are supported.
  24633. The documentation of the library has improved.
  24634. Version 4.0 of dmake from Dennis Vadura <dvadura@plg.uwaterloo.ca> was
  24635. released in Volume 45 Issues 1-27. dmake supports significant enhancements
  24636. over other versions of Make, including:
  24637. support for portable makefiles
  24638. ability to run on many platforms (DOS, UNIX, Apollo, OS/2, Atari, MAC, and
  24639. many others)
  24640. significantly enhanced macro facilities
  24641. sophisticated inference algorithm supporting transitive closure on the
  24642. inference graph
  24643. support for traversing the file system both during making of targets and
  24644. during inference
  24645. %-meta rules for specifying rules to be used for inferring prerequisites
  24646. conditional macros
  24647. proper support for libraries
  24648.  
  24649. parallel making of targets on architectures that support it
  24650. attributed targets
  24651. text diversions
  24652. group recipes
  24653. swapping of itself to disk under MSDOS
  24654. support for the MKS extended argument passing convention
  24655. highly configurable features
  24656. I am always asked for easy-to-use editors for use by novices with Electronic
  24657. Mail packages. One that appeared this time was ee (EasyEdit) from Hugh F.
  24658. Mahon <hugh@nsmdserv.cnd.hp.com>. Posted in Volume 45, Issues 29-33, EasyEdit
  24659. is intended to be a simple, easy-to-use terminal-based screen-oriented editor
  24660. that requires no instruction to use. It supports menus and a help window,
  24661. eight-bit characters and localization, and simple paragraph formatting
  24662. capabilities.
  24663. yorick, a very fast interpreted language designed for scientific computing and
  24664. numerical analysis, was contributed by David H. Munro <munro@icf.llnl.gov> for
  24665. Volume 46, Issues 71-138. Its syntax is similar to C, but without declarative
  24666. statements. yorick provides a very rich selection of multi-dimensional array
  24667. indexing operations. It also features a binary I/O package that automatically
  24668. translates floating-point and integer representations on the machine where it
  24669. is running to and from the format of any other machine. Thus, you can easily
  24670. share binary files between, for example, Cray YMPs and DEC Alphas, or "teach"
  24671. yorick to read existing binary databases. yorick also offers an interactive
  24672. graphics package based on X-Window. X-Y plots, quadrilateral meshes, filled
  24673. meshes, cell arrays, and contours are supported. Finally, you can embed
  24674. compiled routines in custom versions of yorick to solve problems for which the
  24675. interpreter is too slow.
  24676. With the rash of WWW servers on the net, checking the pages for proper syntax
  24677. before publishing them is difficult. Several HTML authoring systems have been
  24678. released, and now Henry Churchyard <churchh@uts.cc.utexas.edu> has contributed
  24679. htmlcheck for Volume 47, Issues 48-54. Version 4.0. The program checks for
  24680. quite a number of possible defects in the HTML (Hyper-Text Mark-up Language)
  24681. version 2.0 SGML files used on the World-Wide Web. htmlcheck is easy to use,
  24682. and gives lots of information including advice about many stylistically bad
  24683. practices. It can do local cross-reference checking and generate rudimentary
  24684. reference-dependency maps. htmlcheck will run on any platform for which an awk
  24685. or perl language interpreter is available.
  24686. This release of htmlchek also includes a number of supplemental utilities,
  24687. including htmlsrpl.pl, the HTML-aware search-and-replace program. This utility
  24688. uses either literal strings or regular expressions; acts either only outside
  24689. HTML/SGML tags, or only within tags; can he restricted to operate only within
  24690. and/or only outside specified elements; and can also uppercase tag names.
  24691. Other utilities are as follows:
  24692. makemenu Makes simple menu for HTML files, based on each
  24693.  file's <TITLE>; this utility can also make a simple
  24694.  table of contents based on <H1>-<H6> headings
  24695. xtraclnk.pl Extracts links/anchors from HTML files; isolates
  24696.  text contained in <A> and <TITLE> elements
  24697. dehtml Removes all HTML markup, preliminary to spell
  24698.  check
  24699. entify Replaces high Latin-1 alphabetic characters with
  24700.  ampersand entities for safe 7-bit transport
  24701. metachar Protects HTML/SGML meta-characters "&<>" in
  24702.  plain text that is to be included in an HTML file.
  24703. htmlcheck is a must for checking out your web pages before releasing them to
  24704. the world.
  24705. Another HTML tool posted recently was m2h from Lawrence A. Coon
  24706. <lac@cs.rit.edu>. Contributed for Volume 47, Issues 3-7, it's a conversion
  24707. tool for translating documents written using troff/me macros into HTML. All
  24708. standard me macros and many troff requests are handled, including paragraphs,
  24709. sections, displays, indexes, delayed text, footnotes, and font specifications.
  24710. m2h also provides support for user and predefined strings, and equations and
  24711. tables, but not for user-defined macros. A local extension to me (.FI) allows
  24712. inclusion of xfig figures that m2h converts to inline GIFs. The special HTML
  24713. characters '<','>', and '&' are escaped and can be used in the me document
  24714. without problems.
  24715. If you want to be able to perform FIP transfers with a C program, then libftp
  24716. from Oleg Orel <orel@oea.ihep.su> is what you need. Posted in Volume 47,
  24717. Issues 29-36, libftp is a simple C interface to the FTP protocol. The library
  24718. contains FtpConnect(host), FtpLogin(user, passw, acct), FtpGet(File),
  24719. FtpDir(filespec), FtpDebug(handler), FtpFullOpen(remote or local, read or
  24720. write), FtpError(handler) and many other functions. Also included is uftp
  24721. (universal file transfer program), which is a user-interactive and
  24722. non-interactive program to the ARPANET File Transfer Protocol (RFC959).The
  24723. uftp client allows the user to transfer files, and groups of files in
  24724. foreground and background modes.
  24725. On the patch front, dist-3.0, the Perlbased configuration script generator and
  24726. related toolset received several patches over the past few months. As usual
  24727. these patches added modules to the configuration library, fixed some bugs, and
  24728. extended the portability of other modules. It also added support for the use
  24729. of Perl 5.0 to the dist toolset. The six patch sets covering patches #27-48
  24730. appeared in the following: Volume 43 Issues 9-11, 42-43, 46-49, Volume 45
  24731. Issues 41-48, 55 and Volume 47 Issues 20-23.
  24732. mailagent, the Perl-based mail thrower, received several patches during this
  24733. interval. Included are Patch 12-16: Volume 44, Issues 66-69, Patch 17-19:
  24734. Volume 44 Issues 135-137, and Patch 19-22: Volume 45 Issues 49-53, Patch 23:
  24735. Volume 45, Issue 56, Patch 24-26: Volume 47 Issues 9-11, Patch 27-29: Volume
  24736. 47 Issues 55-57. The list of new features and bug fixes is too long to mention
  24737. in the space I have available but the biggest are the switch to Dist 3.0 and
  24738. Perl 5 support.
  24739.  
  24740.  
  24741. Shell Menus Revisited
  24742.  
  24743.  
  24744. A commonly recurring theme in UNIX utilities is a menuing system to make tasks
  24745. easier for users. Mike Howard <how%milhow1@uunet.uu.net> contributed
  24746. simple_menu-3.1 to comp.sources.unix for Volume 28, Issues 125-133 with
  24747. patches in Issues 202-204. simple_menu makes it easier to create, maintain,
  24748. modify, and extend menus of common operating system tasks for users. Typically
  24749. users need to run fairly complex pipelines and/or shell scripts. Command-line
  24750. execution is not a viable option, especially for clerical personnel and even
  24751. more so for executive personnel. (This is especially true for infrequently
  24752. executed tasks.)
  24753. Writing menus in shell script is problematic for a variety of reasons. For
  24754. example, writing the prompt-request-response-execute/error loop for the
  24755. zillionth time is a drag. Modifying a shell script menu often breaks the code,
  24756. and the shell menus become excessively large, so it's difficult to even read
  24757. and understand the old code in order to make modifications.
  24758. This program greatly alleviates such problems by introducing the following
  24759. modifications/additions:
  24760. It implements the prompt-request-response-execute loop in a relatively clean
  24761. manner.
  24762. It embeds the shell script that does the work in a higher level language. This
  24763. keeps the individual shell scripts small, isolates individual tasks from each
  24764. other, makes them easier to find, and allows each shell script to execute in
  24765. its own isolated environment, so that modifying one script does not break
  24766. another.
  24767. Each settable shell variable has a user displayed prompt associated with it
  24768. and may have a default value, so that users never have to deal with the shell
  24769. or options in any cryptic way.
  24770. It supports sub-menus.
  24771. It supports a minimal set of menu cosmetics.
  24772. One new concept in computer communications is messenger-based systems.
  24773. Messengers are protocol-unspecific instruction sequences that replace the
  24774. protocol-specific messages on which ordinary computer communication protocols
  24775. are based. Christian Tschudin <tschudin@cui.unige.ch> has contributed m0 for
  24776. Volume 28, Issues 51-62. m0 (M-Zero) is a new high-level prograrmming language
  24777. and execution environment designed for the messenger-based provision of
  24778. computer communication services. As a language, it's very much analogous to
  24779. PostScript. This experimental package contains a complete m0 interpreter and a
  24780. 30-page report on m0, providing a brief introduction to the concept of
  24781. messenger-based computer communications, and containing the m0 language manual
  24782. and format definition.
  24783. If you need to perform syslog like functions, but you are not running TCP/IP,
  24784. Arnold D. Robbins's <arnold@skeeve.atl.ga.us> simple-syslog is for you. It's a
  24785. complete BSD-4.4 compatible syslog, but instead of using IP, it writes to
  24786. stderr. This new update uses ANSI C and stdarg.h, and appears in Volume 28,
  24787. Issue 63.
  24788. The UNIX utility from is very limited. Johnson Michael Earls
  24789. <darkfox@netcom.com> has contributed a new version of his frowmho utility for
  24790. Volume 28, Issues 92-93. Instead of just listing who your mail is from,
  24791. fromwho tells you the total number of messages received; how many are new; and
  24792. for each person who sent you mail, how many messages they sent, how many of
  24793. those are new, and the subjects of the messages. fromwho supports both
  24794. sendmail and MMDF-style mailboxes.
  24795. TCP/IP networks introduced a BOOTP server to allow diskless nodes to download
  24796. their configuration and boot images. Over the years many extensions to the
  24797. original BOOTP server have been written by various OS vendors. Gordon W. Ross
  24798. <gwr@mc.com> has combined all of the extensions and bug fixes into a single
  24799. server and contributed that server, bootp-2.4.0 for Volume 28, Issues 115-118
  24800. with a patch in Issue 119. It combines BOOTP, a bootp gateway program, NetBSD,
  24801. Columbia, Solaris, SVR4, and HP extensions along with full support for names
  24802. as well as IP numbers.
  24803. X has xlock to lock the screen from unauthorized use and to provide a screen
  24804. saver. Now standard terminals can have slock-1.1 from Wes Bachman
  24805. <wbachman@chop.isca.uiowa.edu>. Contributed for Volume 28, Issue 154, slock
  24806. locks a user's terminal on a UNIX system, using the curses capabilities as an
  24807. interface. Unlike the standard lock command, this package includes a number of
  24808. enhancements, including incorrect password entry logging and a screen saver to
  24809. enhance security when the user is away from his/her computer.
  24810. The output of ls -lR is not very useful when searching for files. It only
  24811. provides the file name on the detail lines with directory lines preceding each
  24812. new directory. This format is hard to use with grep. Dennis O'Neill
  24813. <denio@scubed.scubed.com> solves this problem with fullpath-1.1.0 in Volume
  24814. 28, Issue 146. full path-1.1.0 converts the standard ls-lR output format
  24815. /home/denio/fullpath-1.1.0:
  24816. -rw-r--r-- 1 denio 5945 Jun 23 11:08 fullpath.c
  24817. to
  24818. 1992/06/23 5945 /home/denio/fullpath-1.1.0/fullpath.c
  24819. allowing grep once again to find the files. This is especially useful with the
  24820. ls -lR files retrieved from ftp sites.
  24821.  
  24822.  
  24823. Out of Space
  24824.  
  24825.  
  24826. I am out of space for this month, but I've caught up on a large part of the
  24827. backlog. Next time I'll highlight what's new and catch up on the backlog in
  24828. the X sources group.
  24829.  
  24830.  
  24831.  
  24832.  
  24833.  
  24834.  
  24835.  
  24836.  
  24837.  
  24838.  
  24839.  
  24840.  
  24841.  
  24842.  
  24843.  
  24844.  
  24845.  
  24846.  
  24847.  
  24848.  
  24849.  
  24850.  
  24851.  
  24852.  
  24853.  
  24854.  
  24855.  
  24856.  
  24857.  
  24858.  
  24859.  
  24860.  
  24861.  
  24862.  
  24863.  
  24864.  
  24865.  
  24866.  
  24867.  
  24868.  
  24869.  
  24870.  
  24871.  
  24872.  
  24873.  
  24874.  
  24875.  
  24876.  
  24877.  
  24878.  
  24879.  
  24880.  
  24881.  
  24882.  
  24883.  
  24884.  
  24885.  
  24886.  
  24887.  
  24888.  
  24889.  
  24890.  
  24891.  
  24892.  
  24893.  
  24894.  
  24895. Editor's Forum
  24896. I'm still jet lagged from my latest visit to Japan as I write these words. My
  24897. son, Geoffrey, and I spent a long weekend in Kyoto -- a trove of Japanese
  24898. culture and history mercifully spared by the Great Hanshin Earthquake. We also
  24899. spent several days in Tokyo while I conducted the business that nominally
  24900. justified our whirlwind visit.
  24901. Geoffrey is fifteen and a video-game enthusiast of long standing. I think he
  24902. enjoyed the consumer electronic marvels on sale in Akihabara (a shopping
  24903. district in Tokyo) at least as much as the ancient temples, shrines, and
  24904. gardens. He brought home a suitcase half full of gadgets, and still more
  24905. incentive to keep learning his Japanese.
  24906. What I learned was equally interesting, at least for my world. The market for
  24907. embedded systems is large and growing in Japan. An important segment of that
  24908. market is video games. And an important trend in that market segment is away
  24909. from programming in assembly language toward developing video games in C. Game
  24910. systems are finally powerful enough, and complex enough, that the clear
  24911. benefits of a higher-level language outweigh the equally clear costs.
  24912. C++ is still on the horizon for these folks, who still begrudge lost bytes and
  24913. microseconds in a very competitive marketplace. But a separate sector of the
  24914. Japanese market is happy at the prospect of better support for Japanese text
  24915. processing in C++. Two of the more ambitious proposals adopted into the draft
  24916. C++ Standard this past year have Japanese authorship. Both go a long way
  24917. toward putting Kanji text processing on an equal footing with ASCII text in
  24918. the Standard C++ library. Folks who write large applications in C++ now face a
  24919. happier prospect for manipulating text based on large character sets.
  24920. I always enjoy my visits to Japan. On a personal basis, I like many aspects of
  24921. the culture and its heritage. Professionally, I like the dynamism and
  24922. competition the Japanese bring to the business of software development. This
  24923. trip was particularly fun, however, because I got to see many things anew from
  24924. the perspective of an active fifteen-year-old. Our technological innovations
  24925. are becoming his cultural heritage.
  24926. P.J. Plauger
  24927. pjp@plauger.com
  24928.  
  24929.  
  24930.  
  24931.  
  24932.  
  24933.  
  24934.  
  24935.  
  24936.  
  24937.  
  24938.  
  24939.  
  24940.  
  24941.  
  24942.  
  24943.  
  24944.  
  24945.  
  24946.  
  24947.  
  24948.  
  24949.  
  24950.  
  24951.  
  24952.  
  24953.  
  24954.  
  24955.  
  24956.  
  24957.  
  24958.  
  24959.  
  24960.  
  24961.  
  24962.  
  24963.  
  24964.  
  24965.  
  24966.  
  24967.  
  24968.  
  24969.  
  24970.  
  24971.  
  24972.  
  24973.  
  24974.  
  24975.  
  24976.  
  24977.  
  24978.  
  24979.  
  24980.  
  24981. New Products
  24982.  
  24983.  
  24984. Industry-Related News & Announcements
  24985.  
  24986.  
  24987.  
  24988.  
  24989. HP Announces Standard Template Library Accepted by ANSI/ISO
  24990.  
  24991.  
  24992. Hewlett-Packard Company has announced that its Standard Template Library (STL)
  24993. has been accepted by the ANSI/ISO Standards Committee as a part of the
  24994. international standard for C++. HP has placed its implemenatation of STL in
  24995. the public domain. STL, designed by HP laboratories' researchers Alexander
  24996. Stepanov and Meng Lee, provides a set of programming guidelines and a
  24997. collection of generic software components.
  24998. STL contains a broad set of algorithms that perform the most common kind of
  24999. data manipulations programmers use, including searching, sorting, merging,
  25000. copying, and transforming. The algorithms work with data types residing in
  25001. different data structures. For example, the find algorithm in STL works on
  25002. lists, vectors, files, or data structures for which the concept of finding
  25003. makes sense.
  25004. STL also formally defines iterator, the standard data-accessing concept. STL
  25005. defines several categories of iterators, each providing a different way of
  25006. accessing data. A forward iterator guides the algorithm through the data from
  25007. beginning to end. A bi-directional iterator lets the algorithm move forward
  25008. and backward. A random-access iterator can jump around. All the algorithms are
  25009. written in terms of these abstract categories. Because of this, one
  25010. implementation of an algorithm works with any type of data structure.
  25011. The implementation of STL may be obtained by anonymous ftp from
  25012. butler.hpl.hp.com in the directory/stl. For more information contact
  25013. Hewlett-Packard Company, 3000 Hanover St., Palo Alto, CA 94304.
  25014.  
  25015.  
  25016. Pure Software Upgrades Quantify
  25017.  
  25018.  
  25019. Pure Software has upgraded Quantify, a tool which analyzes a program's
  25020. run-time behavior. Quantify is based on Pure Software's Object Code Insertion
  25021. (OCI) technology, which counts the number of instruction cycles. Because OCI
  25022. is not dependent on source-code, OCI lets Quantify analyze the entire
  25023. application, including shared and third-party libraries.
  25024. Quantify 2.0 can automatically compare one Quantify run with another. In
  25025. addition, Quantify 2.0 supports multiple-threaded applications including
  25026. Solaris threads, DCE threads, and lightweight processes under SunOS. With
  25027. Quantify 2.0, developers can identify performance details per thread. Other
  25028. features of Quantify 2.0 include a "river of time" graph, point-and-click
  25029. subtree analysis capability, and an interface that includes intuitive menu
  25030. bars and helps.
  25031. Quantify 2.0 supports SPARC workstations running SunOS or Solaris and HP9000
  25032. workstations running HP-UX. Quantify 2.0 is priced at $1,198 per developer,
  25033. with a minimum order of three. Annual upgrades and support are an additional
  25034. $250 per simple license. Existing Quantify users on maintenance will be
  25035. upgraded to Quantify 2.0 for free. For more information contact Pure Software
  25036. Inc., 1309 S. Mary Ave., Sunnyvale, CA 94087; (408) 720-1600; FAX: (408)
  25037. 720-9200; E-mail: info@pure.com.
  25038.  
  25039.  
  25040. Neuron Data Introduces C/S ELEMENTS++
  25041.  
  25042.  
  25043. Neuron Data has introduced C/S ELEMENTS++, a tool for building next-generation
  25044. client/server applications. C/S ELEMENTS++ is based on C/S ELEMENTS 1.5, an
  25045. application development environment for building enterprise client/server
  25046. applications spanning multiple platforms and data sources. In addition to
  25047. graphical interface development and script functions, C/S ELEMENTS provides
  25048. transparent data-access capabilities. C/S ELEMENTS 1.5 includes a
  25049. data-source-independent, extensible API that lets developers manage
  25050. connections, queries, joins, and updates across multiple data sources. C/S
  25051. ELEMENTS provides read-write native access to Oracle, Sybase, Informix, and
  25052. Ingres databases, as well as access to ODBC-compliant databases such as DB2,
  25053. HP ALLbase/SQL, and SQL Server. Additional features of C/S ELEMENTS 1.5
  25054. include: DBView, a flexible, point-and-click data viewing facility that
  25055. separates the logical view from the physical data source; and Data Link
  25056. Editor, which lets developers define relations between GUI objects and the
  25057. DBView using point-and-click visual programming. C/S ELEMENTS 1.5 also
  25058. includes the following object classes: Connection Object, Query Object, and
  25059. Virtual Table Object. Other features of C/S ELEMENTS 1.5 include support for
  25060. both asynchronous queries and standard database types.
  25061. C/S ELEMENTS++ combines the capabilities in C/S ELEMENTS with C++ support.
  25062. Features of C/S ELEMENTS++ include a separate C++ API that incorporates its
  25063. own C++ libraries, as well as third-party libraries. In addition, C/S
  25064. ELEMENTS++ automatically generates both C++ application code and class
  25065. definitions. Additional C/S ELEMENTS++ features include the following: virtual
  25066. functions that support advanced, object-oriented programming techniques such
  25067. as customization by subclassing, representation of both GUI and non-GUI
  25068. objects as C++ classes, and support for C++ in the Data Access Element via the
  25069. C++ API.
  25070. C/S ELEMENTS++ supports Microsoft Windows, Sun Solaris, SunOS, and
  25071. HP9000/HP-UX systems. C/S ELEMENTS++ is priced at $6,850 for a developer and
  25072. deployment license. There are no run-time fees. For more information contact
  25073. Neuron Data, Palo Alto, CA; (415) 321-4488.
  25074.  
  25075.  
  25076. ASTA Upgrades QA C
  25077.  
  25078.  
  25079. ASTA, Incorporated has upgraded QA C, an automated system which analyzes C
  25080. source code, automates software development code reviews, and automates
  25081. conformance to programming standards. QA C is a configurable analysis system
  25082. used to analyze C source code, check for 800 different types of problems, and
  25083. enforce company-specific programming standards. QA C automates code reviews
  25084. and identifies portability, maintainability, reliability, and stylistic
  25085. issues. By providing an automated mechanism for documenting and controlling
  25086. the quality of source code, QA C can serve as the foundation for Software
  25087. Process Maturity and ISO9000 quality initiatives.
  25088. QA C v4.0 includes support for analyzing C code that contains embedded SQL
  25089. statements. With QA C v4.0, users will be able to parse C code with embedded
  25090. SQL statements prefixed by EXEC SQL or $ and identify syntax errors,
  25091. non-portable SQL, and invalid or questionable coding practices. QA C can
  25092. access relational databases including those from Sybase, Ingres, Oracle, and
  25093. Informix.
  25094. QA C supports UNIX platforms including those from SUN, HP, DEC, and IBM. QA C
  25095. is also available on 386/486/Pentium PCs running SCO UNIX. A QA C v4.0 license
  25096. starts at $6,000. For more information contact ASTA Incorporated, 1 Chestnut
  25097. St., Suites 205/206, Nashua, NH 03060; (800) 350-2782 or (603) 889-2230; FAX:
  25098. (603) 881-3740.
  25099.  
  25100.  
  25101. Excel Upgrades CASE Tool Suites
  25102.  
  25103.  
  25104. Excel Software has upgraded its MacAnalyst and MacDesigner suite of CASE tools
  25105. to generate C/C++, Pascal, Object Pascal, Basic, FORTRAN, or SQL source code
  25106. from software design diagrams. Development teams using object-oriented design,
  25107. structure design, or data modeling can generate header files and function code
  25108. frames for their software. The generated code is target-independent.
  25109. With MacAnalyst and MacDesigner v4.3, object-oriented designers can draw
  25110. diagrams using Booch, OMT, Coad/Yourdon, or Shlaer/Mellor notations with
  25111. dictionary-defined class attributes and operations. Information for attributes
  25112. and operations include data types and function argument lists. Source code
  25113. consisting of C++ or Object Pascal interface files and function code frames
  25114. can be generated.
  25115. Features of the tool suite for database designers include entity-relation
  25116. diagrams (ERDs), customizable Details dialog, Data Dictionary, and foreign
  25117. keys. Also ANSI-standard SQL can be generated to create the database for a
  25118. relational database system (RDBMs). Designers using structured analysis and
  25119. design methods can add function-return data types and function argument lists
  25120. to each model and automatically generate function code frames in either C,
  25121. Pascal, Basic, or FORTRAN.
  25122. Other features of the MacAnalyst and MacDesigner Tool Suite v4.3 include
  25123. customization options and a project menu. Code can be generated for a set of
  25124. design diagrams or added incrementally to existing code files for selected
  25125. diagram objects.
  25126. Prices for the MacAnalyst and MacDesigner CASE tool suite v4.3 range from $995
  25127. to $2,995 per copy. Site licensing is also available. For more information
  25128. contact Excel Software, P.O. Box 1414, Marshalltown, IA 50158; (515) 752-5359;
  25129. FAX: (515) 752-2435; E-mail: casetools@aol.com.
  25130.  
  25131.  
  25132. Cimulus Releases UPData
  25133.  
  25134.  
  25135. Cimulus Automation Systems, Inc. has released UPData, a user-profiling tool.
  25136. UPData includes both a Microsoft Windows DLL and DOS C library. User profiling
  25137. allows developers to observe how users interact with their products. UPData
  25138. monitors a user's actions, such as which option they select, in what order,
  25139. and how often. Information about which commands are most or least commonly
  25140. used or in which operation or dialogs they spend most of their time can be
  25141. collected and logged without interfering with the user or the product.
  25142. Additionally, UPData can track internal warnings and error conditions, and
  25143. lets users add their own suggestions to the log.
  25144. UPData stores the information in a compressed data file, which can be returned
  25145. by a user to the developer for analysis. Based on that data, developers can
  25146. focus their efforts on the areas where they can provide the most benefit. For
  25147. example, defaults and options can be set to streamline the interface and make
  25148. it smoother and more intuitive to use; coded optimization can be made to
  25149. frequently-used routines; and areas where documentation, training, or online
  25150. hints would help most can be identified. When UPData is used to track internal
  25151. code warnings and errors, a problem discovered by a user will automatically
  25152. generate an accurate log of the condition that led up to the error.
  25153. UPData is priced at $79.95 and contains both the Windows and DOS versions. For
  25154. more information contact Cimulus Automation Systems, Inc., 2901 Hubbard Rd.,
  25155. Ann Arbor, MI 48105; (313) 769-4108.
  25156.  
  25157.  
  25158.  
  25159. Abraxas Releases CodeCheck 5.0
  25160.  
  25161.  
  25162. Abraxas Software, Inc. has released CodeCheck 5.0, an analysis tool that
  25163. automates the C++ Quality Assurance process. CodeCheck 5.0 validates software
  25164. standards automatically using Abraxas' Expert System Technology. Quality
  25165. assurance personnel can "teach" CodeCheck 5.0 their company standards and
  25166. CodeCheck 5.0 will validate source code that is in compliance. Using CodeCheck
  25167. 5.0, individual programmers can "correct" their code before submission to the
  25168. Quality Assurance Manager.
  25169. According to Abraxas, CodeCheck 5.0 reads all variants of C/C++ source code.
  25170. CodeCheck 5.0 supports Windows 95, Macintosh, OS/2, and Windows NT operating
  25171. systems. CodeCheck 5.0 also supports UNIX network environments. Prices for
  25172. CodeCheck 5.0 range from $495 to $1,995 per seat. Source code is available for
  25173. license on minicomputer and mainframe sites. For more information contact
  25174. Abraxas Software, Inc., 5530 S.W. Kelly Ave., Portland, OR 97201; (503)
  25175. 244-5253; FAX: (503) 244-8375; E-mail: abraxas@ortel.org.
  25176.  
  25177.  
  25178. MetaWare Offers Compiler Controls
  25179.  
  25180.  
  25181. MetaWare has begun offering C/C++ native and cross compilers with a software
  25182. work-around for the Pentium floating-point division error. MetaWare High C/C++
  25183. for extended DOS, Windows, and OS/2 on Intel platforms offers compiler
  25184. controls that check for the existence of the Pentium floating-point processor
  25185. and generate Intel-compliant versions of Pentium-safe floating-point divide
  25186. code when needed. Quoting Dr. Thomas Pennello, MetaWare's co-founder and vice
  25187. president, "The generated code is safe for all representation of single,
  25188. double, and extended-precision numbers, and the core routines used in the fix
  25189. are fully compliant without the Intel-approved software work-around."
  25190. Additionally, High C/C++ reports the number of times a bad division result
  25191. could occur without Pentium-safe floating-point division code. Developers can
  25192. also analyze increases to execution time that result when using a software
  25193. solution. According to David Wilcox, manager of technical support at MetaWare,
  25194. "The software solution will be made available to customers with current
  25195. versions of High C/C++ for extended DOS, Windows, and OS/2. New versions of
  25196. High C/C++ for extended DOS, Windows, and OS/2 will include the Pentium fix."
  25197. For more information contact MetaWare Incorporated, 2161 Delaware Ave., Santa
  25198. Cruz, CA 95060-5706, (408) 429-6382; FAX: (408) 429-9273; E-mail:
  25199. techsales@metaware.com.
  25200.  
  25201.  
  25202. Visix Ships Galaxy Visual Builder Integration Kit
  25203.  
  25204.  
  25205. Visix Software Inc. has begun shipping the Galaxy Visual Builder Integration
  25206. (VBI) Kit, a product that lets developers customize and extend the Galaxy
  25207. Application Environment. Galaxy is a cross-devlopment platform designed for
  25208. creating scalable applications that are graphical and distributed.
  25209. With the VBI Kit, developers can create and integrate specialized visual
  25210. editors into the Galaxy Visual Resource Builder, letting the developer work
  25211. with custom and specialpurpose objects without leaving the Galaxy visual
  25212. environment. The VBI Kit also lets developers modify standard Galaxy editors
  25213. to operate on extended Galaxy objects within the Visual Resource Builders or
  25214. replace galaxy visual editors with editors tailored to corporate objects, or
  25215. modify the function of standard Galaxy visual editors to ensure that objects
  25216. are used consistently across development groups.
  25217. Galaxy supports UNIX, Macintosh, Microsoft Windows, Windows NT, OS/2, and
  25218. OpenVMS platforms. The Visual Builder Integration Kit is priced at $4,995. For
  25219. more information contact Visix Software Inc., 11440 Commerce Park Dr., Reston,
  25220. VA 22091; (800) 832-8668 or (703) 758-8230; FAX: (703) 758-0233.
  25221.  
  25222.  
  25223. LOOX Introduces LOOX 3.0
  25224.  
  25225.  
  25226. LOOX Software, Inc. has introduced LOOX 3.0, an object-oriented graphics
  25227. development tool for X-Windows. LOOX lets developers create interactive
  25228. graphics for UNIX applications using LOOXMaker, a vector-based graphical
  25229. editor, and LOOXLib, a vector-based, object-oriented C function library.
  25230. Features of LOOX 3.0 include a communications protocol and 15 types of
  25231. two-dimensional charts. In addition, LOOXMaker generates code and includes a
  25232. drawing browser. LOOXLib contains a class of ready-to-use dynamic objects and
  25233. two classes of vector objects, the PARALLELOGRAM class and the SUBDRAWING
  25234. class. LOOXLib also includes methods and resources for creating customizable
  25235. vector objects. The methods include detecting a user-click with the right
  25236. mouse button and designing editable text objects. LOOX 3.0 also includes
  25237. on-line documentation, a tutorial, and source code for various sample
  25238. programs.
  25239. LOOX 3.0 supports SUN OS 4.1.x, Solaris 2.x, HP-UX 9.0, AIX 3.x, SGI IRIX,
  25240. Solaris x86, UNIXWare, SCO, and X Terminals with X11R4 or X11R5. LOOX 3.0
  25241. requires Motif, X11R4, or X11R5. LOOX 3.0 is priced at $9,950 per license.
  25242. There are no run-time fees. For more information contact LOOX Software, Inc.,
  25243. 4962 El Camino Real, Suite 206, Los Altos, CA 94022; (415) 903-0942; FAX:
  25244. (415) 903-9824.
  25245.  
  25246.  
  25247. Kofax Releases KIPP ImageControls
  25248.  
  25249.  
  25250. Kofax Image Products has released KIPP ImageControls v1.0, a suite of Visual
  25251. Basic tools for imaging applications. The suite of VBX-based tools lets users
  25252. develop new systems or image-enable legacy applications with high-volume
  25253. document image processing capabilities. KIPP ImageControls is compatible with
  25254. Microsoft Visual Basic and Visual C++.
  25255. Available in two editions, the Standard Edition of KIPP ImageControls provides
  25256. image scan, print, and display capabilities. The Gold Edition includes
  25257. sophisticated image processing features required in high-volume applications.
  25258. The Gold Edition includes the following image processing features: bar code
  25259. recognition during scanning; image deskewing to align misfed documents
  25260. electronically to improve optical character recognition accuracy; job
  25261. separation detection to identify the start of a new document in batches; and
  25262. text annotation to electronically mark scanning date, time, or other
  25263. information on the image. Both editions support rated-speed scanner operations
  25264. for 100 scanners operating from 10 ppm to 100 ppm, and network printing at up
  25265. to 17 ppm.
  25266. The Standard Edition of KIPP Image Controls is priced at $995. The Gold
  25267. Edition is priced at $2,995. There are no royalty fees or licensing
  25268. restrictions. For more information contact Kofax Image Products, 3 Jenner St.,
  25269. Irvine, CA 92718-3807; (714) 727-1733; FAX: (714) 727-3144.
  25270.  
  25271.  
  25272. Blue Sky Upgrades RoboHELP
  25273.  
  25274.  
  25275. Blue Sky has upgraded RoboHELP, a help-authoring tool for Windows and Windows
  25276. NT. RoboHELP 3.0 is bundled with WinHelp Video Kit and includes the tools
  25277. needed for integrating and playing video and sound into Help systems. Features
  25278. of RoboHELP 3.0 include support for special Word 6.0 features and support for
  25279. European versions of Work 6.0. Other features of RoboHELP 3.0 include four
  25280. HelpCheck tools, a macro hotspot editor, and documentation templates.
  25281. Features of the WinHelp Video Kit include Software Video Camera, a full-motion
  25282. screen action recording program; Video Wizard for adding video and sound to
  25283. Help projects; a video tester for previewing video and sound files; WinHelp
  25284. Video/Sound Player for video and sound playback in Help files; and Video for
  25285. Windows 1.1d Runtime.
  25286. The RoboHELP 3.0 and WinHelp Video Kit bundle is priced at $499. The WinHelp
  25287. Video Kit can be purchased separately for $99. WinHelp Video Kit includes a
  25288. royalty-free license to distribute the video/sound player DLL. Registered
  25289. users of RoboHELP 2.0 and 2.6 can upgrade to the RoboHELP 3.0 and WinHelp
  25290. Video Kit for $129. For more information contact Blue Sky Software Corp., 7486
  25291. La Jolla Blvd., Suite 3, La Jolla, CA 92037; (800) 677-4946 or (619) 459-6365;
  25292. FAX: (619) 459-6366.
  25293.  
  25294.  
  25295. Microway Announces Fix for FDIV Bug
  25296.  
  25297.  
  25298. Microway has announced a software fix for the Intel FDIV bug. For execution
  25299. speed reasons, Microway NDP compilers generate inline x87 code. According to
  25300. Microway, the inline fix for FDIV executes in less that 30 cycles, does not
  25301. substantially increase the execution times of floating-point intensive
  25302. applications, and can be adjusted to guarantee the precision of single-real,
  25303. double-real, and temporary-real operations. This fix only works for
  25304. applications which have been recompiled with Microway compilers.
  25305. The software fix will be available free of charge to owners of 386, 486, and
  25306. Pentium optimized versions of NDP C/C++, NDP FORTRAN, and NDP Pascal. For more
  25307. information contact Microway, Research Park, Box 79, Kingston, MA 02364; (508)
  25308. 746-7341l FAX: (508) 746-4678; E-mail: stevef@microway.com.
  25309.  
  25310.  
  25311. HyperAct Announces Three Products
  25312.  
  25313.  
  25314.  
  25315. HyperAct has announced three products, Interactive Help for Windows (IH), an
  25316. upgraded version of PASTERP, and XSpawn. IH is a WinHelp DLL extension
  25317. language, which adds interactive forms and dialogs to WinHelp titles and links
  25318. them dynamically to external resources. IH monitors and controls WinHelp
  25319. activities, executes the standard WinHelp macros, and calls the WinHelp
  25320. "Callback" internal functions API.
  25321. PASTERP is a Pascal-Like interpreter with an embedded interface to C++ and
  25322. Borland Pascal programs. The PASTERP development kit includes: Object, the
  25323. PASTERP parse and execution engine implemented as a hierarchy of Borland
  25324. Pascal Object classes; DLL, the PASTERP parser and engine implemented in a DLL
  25325. that can be interfaced using the DLL's API; object-oriented frameworks; and
  25326. support for dynamic functions.
  25327. The XSpawn library contains a set of functions, which detect spawned program
  25328. termination and its exit code, support synchronized spawning under WIN-OS/2
  25329. and Windows NT, and detect termination and exit code of OS/2 and Win 32
  25330. applications. XSpawn is shipped with header files and example programs for
  25331. C/C++, Borland Pascal, and Visual Basic.
  25332. IH is priced at $149. PASTERP is priced at $350 with royalty fees. XSpawn is
  25333. priced at $99 with full basic source code included. For more information
  25334. contact HyperAct, Inc., P.O. Box 5517, Coralville, IA 52241; (319) 351-8413;
  25335. FAX: (319) 351-8413; CompuServe: 76350,333; Internet: rloewy@panix.com.
  25336.  
  25337.  
  25338. Rational Integrates Rational Rose
  25339.  
  25340.  
  25341. Rational Software Corporation has integrated its Rational Rose family of
  25342. analysis and design tools with its SoDA documentation tool. Developers can
  25343. automatically generate software documentation from their analysis and design
  25344. models, and from their code. The Rational Rose family is a suite of graphical,
  25345. object-oriented tools for analysis, design, and implementation of software
  25346. applications. The tools support an iterative development cycle and includes
  25347. capabilities for generating and reverse engineering C++, Ada, SQL Windows, and
  25348. ObjectPro code.
  25349. SoDA is available as an option to Rational Rose and supports workstations
  25350. running SunOS and Solaris. Rational also plans an AIX version. SoDA is priced
  25351. at $6,000. For more information contact Rational Software Corporation, 2800
  25352. San Tomas Expressway, Santa Clara, CA 95051-0951; (800) 728-1212 or (408)
  25353. 496-3600; FAX: (408) 496-3636; E-mail: product_info@rational.com.
  25354.  
  25355.  
  25356. Xcc Releases Revision Control Engine
  25357.  
  25358.  
  25359. Xcc Software has released the Revision Control Engine (RCE), a multi-platform
  25360. programming library, which includes an API for revision and version control.
  25361. RCE is based on Walter F. Tichy's RCS tool. RCE's API provides calls for
  25362. version and version information, project management, interface dialogue, and
  25363. error messages. The user interface can either be command or graphical
  25364. (Microsoft Windows or OSF/Motif). Features of RCE include seamless integration
  25365. with other application-development products and a delta algorithm. RCE stores
  25366. deltas instead of full versions of data formats such as binary files, images,
  25367. and sound.
  25368. RCE supports Windows, Windows 95, Windows NT, OS/2, and UNIX. RCE is priced at
  25369. $1,990 for Windows , or $3,330 for UNIX. For more information contact ESC,
  25370. P.O. Box 1982, Lawrence, KS, 66044; (913) 832-2070;FAX: (913) 832-8787; or Xcc
  25371. Software, Durlacher Allee 53, D-76131 Karlsruhe, Germany; +49 721-616474; FAX:
  25372. +49 721-621384; E-mail: rce@xcc-ka.de.
  25373.  
  25374.  
  25375. BSO/TASKING Announces Starter Pack And Power Pack
  25376.  
  25377.  
  25378. Boston Systems Office/Tasking has announced the Starter Pack and Power Pack.
  25379. The Starter Pack is an ANSI-C-based tool kit which includes a binary-mode
  25380. compiler and a simulator debugger for direct migration of 8051 applications to
  25381. the MCS 251 architecture. The Power Pack, a source-mode compiler, produces
  25382. code for the 251 architecture. The Power Pack includes an ANSI C source-mode
  25383. compiler with 251-style extensions, which support the instruction set and 16Mb
  25384. address space of the MCS 251 microcontroller. Both the Starter Pack and Power
  25385. Pack include the CrossView Windows 251 Simulation Debugger for C and assembly
  25386. debugging.
  25387. The Starter Pack supports PCs. The Power Pack supports PCs, Sun SPARC, Sun
  25388. Solaris, and HP9000 HP-UX. Pricing for the Starter Pack starts at $395. For
  25389. more information contact Boston Systems Office/Tasking, 333 Elm St., Dedham,
  25390. MA 02026; (617) 320-9400; FAX: (617) 320-9212.
  25391.  
  25392.  
  25393. CRC Press Introduces Numerical Library
  25394.  
  25395.  
  25396. CRC Press, Inc. has introduced a numerical library in C for engineers and
  25397. scientists. The library lets users solve numerical problems in areas of linear
  25398. algebra, ordinary and partial differential equations, optimization, parameter
  25399. estimation, and special functions of mathematical physics. Features of the
  25400. numerical library include ANSI C for portability, a diskette with source code,
  25401. a compact modular structure for implementation on a PC, and 130 pages of
  25402. worked examples.
  25403. For more information contact CRC Press, Inc., 2000 Corporate Blvd. N.W., Boca
  25404. Raton, FL 33431; (800) 272-7737 ext. 2232 or (407) 994-0555; FAX: (800)
  25405. 374-3401.
  25406.  
  25407.  
  25408.  
  25409.  
  25410.  
  25411.  
  25412.  
  25413.  
  25414.  
  25415.  
  25416.  
  25417.  
  25418.  
  25419.  
  25420.  
  25421.  
  25422.  
  25423.  
  25424.  
  25425.  
  25426.  
  25427.  
  25428.  
  25429.  
  25430.  
  25431.  
  25432.  
  25433.  
  25434.  
  25435.  
  25436.  
  25437. We Have Mail
  25438. Dear Editor:
  25439. Please send me your Author Guidelines for C/C++ User's Journal. I have several
  25440. ideas; however some are sure to melt away once I find out how much work is
  25441. involved.
  25442. Also, on page 95 of the October 94 issue, it says "Some programmers may wish
  25443. to reuse the file_pointer member as a validity flag, instead of using a
  25444. separate member. Although reusing file_pointer may save a byte or two per
  25445. object, I feel that having a separate state member keeps the code more
  25446. readable and maintainable."
  25447. Okay, but you missed an important opportunity to demonstrate how C++ can give
  25448. you the best of both worlds. Go ahead and internally use the file_pointer
  25449. member as the state variable, but use the public interface is_valid() to hide
  25450. this. If, in the future, other reasons for invalid objects come up, it's easy
  25451. to add an explicit state member because the is_valid() calls are already used.
  25452. To me, design trade-offs like this are exactly why C++ is more
  25453. powerful/maintainable than C.
  25454. Thanks and keep up the good work,
  25455. Andy Krassowski
  25456. andyk@sclara.qms.com
  25457. You make a good point about class interface style. -- pjp
  25458. Dear Mr. Plauger,
  25459. I have been reading CUJ for the last two years and I enjoy it and find it very
  25460. useful. It will be very good if all the articles published under "Code
  25461. Capsules" or "Standard C" and "Stepping Up to C++" are made available at one
  25462. place. Especially "Code Capsules" is very interesting. It will be useful to
  25463. have all the articles under "Code Capsules" in a book form or as a special
  25464. issue of CUJ. Do you intend to publish some articles on object-oriented
  25465. programming?
  25466. Yours faithfully,
  25467. J.P. Singh
  25468. Melbourne, VIC 3071
  25469. AUSTRALIA
  25470. Many, if not all, of my "Standard C/C++" columns are also to be found in two
  25471. of my published books, The Standard C Library, and The Draft Standard C++
  25472. Library, both published by Prentice Hall. I have been nagging both Dan Saks
  25473. and Chuck Allison for years to produce books based on their respective columns
  25474. as well. Talented as they are, they are both naturally too busy to find much
  25475. spare time for such pursuits. I keep hoping, nonetheless. R&D Publications
  25476. also continues to explore ways to make the material in their magazines
  25477. available in other useful forms. So you should keep hoping too.
  25478. We'll probably never do an article on object-oriented programming per se. At
  25479. least, it won't be one of those preachy things telling you how to program
  25480. properly, at last. We prefer to run more useful "how to" articles, written by
  25481. practicing programmers, that cover such topics in passing. I persist in the
  25482. belief that pragmatic examples of working code are a better teaching tool. --
  25483. pjp
  25484. Hi,
  25485. I'm curious about a statement in Kenneth Pugh's article "Using C Libraries in
  25486. C++" in the May 1994 issue of CUJ. There is a statement there to the effect
  25487. that in C++ it is the responsibility of the called funtion to clean up the
  25488. stack when returning.
  25489. I'm probably missing something, but I've been sitting here for a while now
  25490. trying to figure out how it's possible to have variable argument lists if the
  25491. called function pops the stack on return. Assuming that the called function is
  25492. called from more than one calling function, how would the compiler know how
  25493. many and what type of arguments to remove from the stack when generating the
  25494. code for the called function? Does it generate a copy of the function
  25495. compatible with every invocation?
  25496. Sorry if I'm missing something obvious.
  25497. Joe Halpin
  25498. jhalpin@netcom.com
  25499. Ken should probably have qualified his statement a bit. It was a longstanding
  25500. practice with C compilers that the caller removed arguments from the stack.
  25501. Hence, it didn't hurt if the caller supplied more arguments than the callee
  25502. expected. The C Standard introduced function prototypes so that, in the
  25503. presence of a prototype at least, the callee could know how to clean the
  25504. stack. The C Standard also requires that a function with a fixed number of
  25505. arguments be called with the proper number. Compilers can thus now tailor
  25506. their calling sequences accordingly. But C still requires the caller to clean
  25507. the stack in one contex -- where the prototype declares that the function
  25508. expects a varying number of arguments.
  25509. C++ is even more restrictive. Every function call must occur in the scope of a
  25510. prototype. Hence, except for the case of a function explicitly declared to
  25511. accepty a varying number of arguments, the callee can reliably clean the
  25512. stack. I hope that added bit of precision helps clarify your understanding. --
  25513. pjp
  25514. Hello Dr. Plauger:
  25515. As a new programmer I find your articles interesting and helpful. I do
  25516. experience some difficulties which I continually learn from due to compiler
  25517. differences.
  25518. In particular, the article "Extending the Windows File Manager" (Sept. 1994,
  25519. pp. 37) by Trevor Harmon needed a bit of modification to compile and function
  25520. properly using Borland V4.0. The compiler didn't like the EXPORTS section in
  25521. the FMEXT.DEF file. All that is needed is to delete the EXPORTS section
  25522. entirely and to add the _export keyword to the LibMain, WEP, ShowSelDlgProc,
  25523. DriveInfoDlgProc, and FMExtensionProc functions in FMNEXT.C. Also, #pragma
  25524. argsused can be used where apropriate to eliminate those pesky "variable not
  25525. used" warnings.
  25526. The new functionality is very interesting and I thank you and Mr. Harmon for
  25527. providing it.
  25528. Michael L. Rohwedder
  25529. (ML-Rohwedder@bgu.edu)
  25530. Thanks for the information. And welcome to the wonderful world of nonportable
  25531. C extensions. -- pjp
  25532. Dear editor,
  25533. I read the article "A Simple Soundex Program" in the September 94 issue and
  25534. discussed it with an associate here. I mentioned the May 1991 article
  25535. referenced in this article about Levenstein Distances. This was titled
  25536. "Inexact Alphanumeric Comparisons" by Hans Zwakenberg.
  25537. I had read that article in 1991 and modified the algorithm some time ago to
  25538. what I felt was a more efficient one. My version uses a single array and few
  25539. other vairables as a sliding window instead of the double subscripted array in
  25540. the origional code. My associate suggested that I send my example back to you,
  25541. so here it is. [It's available on the monthly code disk. -- pjp]
  25542. It consists of a PKZIP file (v2.04g) containing a source module for the LDIST
  25543. code, a header file and two sample programs I wrote to test the functions. I
  25544. wrote an additional list search function using the algorithm to return the top
  25545. N matches (lowest L distances) from a list.
  25546. I work for MCI in the Data Services Division and searching address lists for
  25547. names is an important part of what we do here. Because of this, these
  25548. algorithms, both the L Distance and the Soundex, interest me. Thanks.
  25549. R. Bruce Roberts
  25550. System Development Engineer
  25551. MCI Data Services Division
  25552. Thank you. -- pjp
  25553. Dear Editor:
  25554. Regarding the Sept '94 letter from Lee Shackelford in which he asked of OOP,
  25555. "Why bother?" Lee raises a legitimate question that I don't feel is asked
  25556. often enough in our zeal to try out exciting new technologies.
  25557. Don't get me wrong, I'm not anti-OOP. I see benefits to the technology and
  25558. have applied OOP to my work. But sometimes we adopt complex, new technologies
  25559. in one big gulp without proper planning and proper consideration of the
  25560. effects on our development processes.
  25561. Haphazard introduction of new technologies can lead to disasters, as happened
  25562. to Borland International. Borland committed its projects to use C++, causing
  25563. huge delays in getting their products to market. These delays put Borland into
  25564. deep financial trouble. [1] Perhaps Borland should have tried C++ on a pilot
  25565. project first, then slowly introduced the technology to other projects.
  25566. The moral of this story isn't to stop trying new technologies, just be careful
  25567. doing so. And don't forget to ask "Why bother?" again and again.
  25568. Mason Deaver
  25569. AT&T Network Control Center - West
  25570. Denver, CO 80205
  25571. mcdeaver@attmail.com
  25572. [1] Andrew Binstock, "Objectively Speaking," Viewpoint column, Unix Review,
  25573. June 1994, pg. 7
  25574. Amen. -- pjp
  25575. Dear Mr. Plauger,
  25576. Thank you very much for your illuminating review on the book "Software
  25577. Internationalization and Localization" which appeared in the C Users Journal!
  25578. Our organization, VTT, participates in a European research project, called
  25579. GLOSSASOFT. VTT is a Finnish acronym for Technical Reseach Center of Finland.
  25580. It is a non-profit research organization with 2,600 employees. The partners in
  25581. the GLOSSASOFT project, in addition to VTT, are Open University from U.K.,
  25582. NCSR Demokritos from Greece, Claris from Ireland, HP Hellas from Greece, and
  25583. Bull from France. The aim of the project is to develop methods and guidelines
  25584. for software internationalization and localization.
  25585. VTT's expertise is centered around software development and therefore our
  25586. concern is very much in the same area which you outline in your review. It
  25587. seems that many of the current books and articles on internationalization and
  25588. localization are written by specialists of localization. Good knowledge of
  25589. programming is needed, of course, when internationalization is concerned.
  25590. In addition to our thanks, I would like to ask your kind advice on the
  25591. following subjects:
  25592.  
  25593. what you consider the best sources of information related to the support in C
  25594. for internationalization especially concerning future directions, and
  25595. is there something that you could point out as a useful research effort in
  25596. internationalization issues related to C, taken into account the work that is
  25597. already being done e.g. in ISO?
  25598. Best regards,
  25599. Timo Honkela
  25600. vtt.fi!Timo.Honkela
  25601. I have written several times in these pages about internationalization. See
  25602. the March through May 1991 and the May through July 1993 issues. Much of that
  25603. material is also available in my book, The Standard C Library. Also, The
  25604. Journal of C Language Translation, edited by John Levine (johnl@iecc.
  25605. cambridge.ma.us) frequently prints articles on this topic. And Rex Jaeschke,
  25606. Chair of X3J11, has an article you may find interesting in the pipeline for
  25607. publication in this magazine.
  25608. Ongoing standardization in the area of internationalization is unfortunately
  25609. divided among several groups. Within ISO committee JTC1/SC22 are the working
  25610. groups WG14 (C), WG15 (POSIX), WG20 (internationalization), and WG21 (C++).
  25611. Then there's X/Open and CEN, in Europe. I have trouble keeping up myself. Good
  25612. luck. -- pjp
  25613. Dear Mr. Plauger,
  25614. I have a couple of new tricks that I have recently learned. Having a
  25615. programmer's instinctive desire to see people write code the way I would, I
  25616. thought I might share them.
  25617. The first involves numeric conversion constants. Most of us have encountered
  25618. the everpresent definitions:
  25619. #define DEGREES_ TO_RADIANS M_PI/180.
  25620. #define RADIANS_TO_DEGREES 180./M_PI
  25621. I recently had reason to also deal with mils (6,400 to a circle), bams
  25622. (0x10000 to a circle) and sectors (we break the circle into 256 sectors for
  25623. processing). (For the curious, this was part of a radar simulation.) Rather
  25624. than defining 20 different conversion constants, I merely defined 180 degrees
  25625. in each of the five units as follows:
  25626. // 1/2 circle in various units
  25627. // Usage example:
  25628. // radval = degval * (RADIANS/DEGREES)
  25629. #define BAMS ((float)0x8000)
  25630. #define MILS 3200.0
  25631. #define DEGREES 180.0
  25632. #define SECTORS 128.0
  25633. #define RADIANS M_PI
  25634. I could then easily and naturally perform any conversion by combining the
  25635. necessary constants, much as I learned in college physics:
  25636. sectval = milval * (SECTORS/MILS);
  25637. radval = sectval * (RADIANS/SECTORS);
  25638. and so forth. This is extremely flexible, and suffers no loss of readability.
  25639. The parentheses insure that a compiler with decent optimization will
  25640. pre-compute the division at compile time, producing a single multiply. Without
  25641. the parentheses, the expression may generate a multiply and a divide. I tested
  25642. this with Borland C++ 4.0, and the parentheses do save a run-time divide
  25643. there.
  25644. The second trick is a way around a difficult, but fairly common, coding
  25645. problem. I'm sure we have all run into loops like the following:
  25646. fgets(string, MAXLEN, stdin);
  25647. while (string[0] == '\n')
  25648. {
  25649. puts("Entering a blank line won't cut
  25650. it.\n");
  25651. fgets(string, MAXLEN, stdin);
  25652. }
  25653. The problem, of course, is the repetition of the fgets call. This duplication
  25654. is undesirable. It's too easy to change one of the calls and miss the other
  25655. (especially in more realistic examples).
  25656. One solution is to change this to the following:
  25657. for (fgets(string, MAXLEN, stdin);
  25658. string[0] == '\n';
  25659. fgets(string, MAXLEN, stdin))
  25660. puts("Entering a blank line won't cut
  25661. it.\n");
  25662. This really doesn't cure the problem, and it abuses the iterative intent of
  25663. the for loop.
  25664. My favorite solution to this problem is:
  25665. while (fgets(string, MAXLEN, stdin),
  25666. string[0] == '\n')
  25667. puts("Entering a blank line won't cut
  25668. it.\n");
  25669. This is an unconventional solution, but seems to me a very effective use of
  25670. the comma operator. For cases like this, it may be one of the best solutions
  25671. for a sticky problem. It is certainly better than:
  25672. while (fgets(string, MAXLEN, stdin)[0] == '\n')
  25673. puts("Entering a blank line won't cut
  25674. it.\n");
  25675. which will also work, but may be less welcome in a professional environment.
  25676. Thanks for your time, and apologies to all the people who thought of these
  25677. ideas before I did.
  25678. Brian Tagg
  25679. I like your use of manifest constants to handle multiple units. When it comes
  25680. to fgets, however, I'm a bit more paranoid. I always check to see if it
  25681. returns a null pointer. And I worry about whether it truncates a long line.
  25682. Otherwise, I approve of your concern with developing a good and consistent
  25683. programming style. -- pjp
  25684. Dear Editor,
  25685. This is a response to Edward Bell's letter (CUJ, March 1995), in which he asks
  25686. for a program to generate random English pronounceable proper names.
  25687. I assume that you want to randomly generate the characters that make up the
  25688. names and not just pick names randomly from a list.
  25689. I've tried this a few times. The only solution I like so far is to use a list
  25690. of sample names to tabulate some statistics before generating the random
  25691. names. I use about a 3- or 4-dimensional array to store the occurrences of the
  25692. current character based on the previous characters.
  25693. /* Something like ... */
  25694. c0 = getchar();
  25695.  
  25696. a[c2][c1][c0]++;
  25697. c2 = c1;
  25698. c1 = c0;
  25699. You probably want to set the previous characters to spaces between each name
  25700. to eliminate correlation between names.
  25701. I'm usually willing to handle punctuation and case on my own, so I just make
  25702. each dimension 27 elements, one for each letter of the alphabet, plus one more
  25703. for spaces and everything else. Most of the elements will have a frequency of
  25704. zero, so you should save memory if a sparse array is used. Three dimensions
  25705. should give fairly pronounceable names; four dimensions will probably start
  25706. duplicating real names regularly.
  25707. To generate the names, total the occurrences for each posibility for the
  25708. current character based on the previous characters and then generate a random
  25709. number less than the total, and then find the character.
  25710. /* Something like ... */
  25711. c2 = c1;
  25712. c1 = c0;
  25713. n = 0;
  25714. for (i = 0; i < dim_siz; i++) n +=
  25715. a[c2][c1][i];
  25716. r = rand() % n;
  25717. for (i = 0; i < dim_siz; i++) {
  25718. if (r < a[c2][c1][i]) {
  25719. c0 = i; /* "c0" is the next char. */
  25720. break;
  25721. }
  25722. r -= a[c2][c1][i];
  25723. }
  25724. putchar(c0);
  25725. A potential problem is that the random names tend to vary more in length than
  25726. real names. If this is a problem, you can vary the weight of the space
  25727. character based on the current name length.
  25728. References: (Old) Cryptography books often have information about
  25729. frequently-occurring character sequences. Data compression algorithms based on
  25730. frequently-occurring character sequences might be a good source for efficient
  25731. storage methods.
  25732. Sheldon
  25733. Thanks -- pjp
  25734. Dear Mr. Plauger,
  25735. I have just completed a course in advanced C++ at the University of California
  25736. Extension Division. In the last class, we discussed new features of C++
  25737. including namespaces. That discussion brought up the fact that when the
  25738. directive form is used (i.e. "using namespace") all classes, variables and
  25739. functions declared in that namespace will be put into the global scope. It
  25740. seems that this could cause problems if there were already items with the same
  25741. name in global scope.
  25742. Is it possible to do some sort of name mangling on the variables etc. in the
  25743. namespace when the using namespace declaration is used so that these items
  25744. retain their uniqueness and thus can be accessed by their symbolic name only?
  25745. The items already existing at the global scope can then be accessed by using
  25746. the scope resolution operator.
  25747. Also, when do the namespace items get removed from global scope? Is it
  25748. delimited by a { } pair or something else?
  25749. Thanks,
  25750. Dave Goldstein
  25751. Pittsburgh, PA
  25752. I believe there are rules that favor items declared in the parent namespace
  25753. over those imported via a using declaration, but you're still correct.
  25754. Programmers will encounter conceptual problems when names collide. Sometimes
  25755. they'll expect to get the imported name, sometimes they'll expect to get the
  25756. native name, and sometimes they'll want a diagnostic. Since no commercial
  25757. compilers exist that implement namespaces, to my knowledge, it's hard to do
  25758. experiments to see what works and what doesn't.
  25759. As for your last question, like any other declaration, the names it introduces
  25760. go out of scope at the end of the block containing the declaration.
  25761. Thanks for writing,
  25762. P.J. Plauger
  25763. Hi
  25764. We are New at Visual C++ and have a problem.
  25765. I need to create a modal dialog, that has no resource template, because I read
  25766. the window's size and position from an ASCII file, as well as a fields
  25767. definition -- these fields are created dynamically.
  25768. When I am building the above-mentioned window, I already have another one
  25769. which is modal. I need to disable the first one and then show the window I am
  25770. putting up from the ASCII file.
  25771. For creating the window I used the CreateExt function, This window is derived
  25772. from CWnd instead of CDialog.
  25773. Can you gimme a clue as to how to create this window?
  25774. I send you my regards.
  25775. Juan Carlos Flores
  25776. (jflores@macosa.com.ec)
  25777. MACOSA - NCR
  25778. Alpallana 289 y Almagro
  25779. Quito - Ecuador South America
  25780. Veronica Burbano
  25781. (vburbano@macosa.com.ec)
  25782. MACOSA - NCR
  25783. Alpallana 289 y Almagro
  25784. Quito - Ecuador South America
  25785. Any Windows gurus out there? -- pjp
  25786.  
  25787.  
  25788.  
  25789.  
  25790.  
  25791.  
  25792.  
  25793.  
  25794.  
  25795.  
  25796.  
  25797.  
  25798.  
  25799.  
  25800.  
  25801.  
  25802.  
  25803.  
  25804.  
  25805.  
  25806.  
  25807.  
  25808.  
  25809.  
  25810.  
  25811.  
  25812.  
  25813.  
  25814.  
  25815.  
  25816.  
  25817.  
  25818.  
  25819.  
  25820.  
  25821.  
  25822.  
  25823.  
  25824.  
  25825.  
  25826.  
  25827.  
  25828.  
  25829.  
  25830.  
  25831.  
  25832.  
  25833.  
  25834.  
  25835.  
  25836.  
  25837.  
  25838.  
  25839.  
  25840.  
  25841.  
  25842.  
  25843.  
  25844.  
  25845.  
  25846.  
  25847.  
  25848.  
  25849.  
  25850.  
  25851.  
  25852.  
  25853.  
  25854.  
  25855. WinJES -- A Windows Sockets Example
  25856.  
  25857.  
  25858. Kevin Gilhooly
  25859.  
  25860.  
  25861. Kevin Gilhooly is with RIS Information Services in Dallas, working on a
  25862. variety of PC-based client server projects. He can be reached at kjg@dfw.net.
  25863.  
  25864.  
  25865.  
  25866.  
  25867. Introduction
  25868.  
  25869.  
  25870. Windows Sockets is an open network programming interface for Microsoft
  25871. Windows. Adapted from Berkely Sockets (which works with UNIX systems), Windows
  25872. Sockets enables two programs to establish a bidirectional connection. The
  25873. programs may be on the same or different computers.
  25874. In this article I present WinJES, a small Microsoft Windows program that lets
  25875. a user issue a command on a remote Windows machine. WinJES uses the Windows
  25876. Sockets interface for communications between PCs across a network. The program
  25877. is both a client and a server, and one copy can exchange messages either with
  25878. itself or with a copy on a different machine.
  25879. I wrote the original WinJES program using Microsoft's Visual C++ 1.1 and
  25880. NetManage's Chameleon SDK under Windows for Workgroups 3.11. The program uses
  25881. the Chameleon TCP/IP stack. I've also compiled it under the Windows NT 3.5
  25882. Workstation's native TCP/IP, with Visual C++ version 2.0. Any
  25883. Windows-Sockets-compliant developer's kit and Windows C compiler should be
  25884. able to compile the source code.
  25885.  
  25886.  
  25887. Establishing a Protocol
  25888.  
  25889.  
  25890. To communicate with each other, two programs must use a protocol. Windows
  25891. Sockets programs use either the Transmission Control Protocol (TCP) or User
  25892. Datagram Protocol (UDP) (see sidebar, "Understanding Sockets and Protocols.").
  25893. WinJES uses UDP. Unlike TCP, two machines communicating via UDP do not
  25894. establish a "connection." Rather, they send messages back and forth without
  25895. acknowledgements. When WinJES starts, it listens for requests from other
  25896. machines. A user on a remote machine can then submit commands to be executed
  25897. on the local machine. In many environments, a program can simply wait for
  25898. input from a remote partner. In UNIX, or on a dedicated DOS machine, a
  25899. standard UDP server program with no error checking would look like Listing 1.
  25900. For various reasons, a Windows 3.x server cannot be quite so simple. For one
  25901. thing, because Windows 3.x is non-preemptive, programs must yield the
  25902. processor to Windows after a reasonable time, or no other processes will be
  25903. able to run. Also, Windows is based on a messaging model. When any event
  25904. occurs, Windows sends a message to a window procedure (code tied to a
  25905. particular window); the code must process the message as quickly as possible
  25906. and return to a passive state. Therefore, the above code must be modified for
  25907. Windows.
  25908. WinJES does the real work inside a message loop. Before entering a message
  25909. loop, the program must register a window class and create a window. The window
  25910. class ties the window to a window procedure (the code that processes its
  25911. messages.) Since WinJES uses the Windows Sockets functions, it must initialize
  25912. Windows Sockets at the start of the program (via function WSAStartup), and
  25913. terminate Windows Sockets when the program exits (via function WSACleanup).
  25914. Listing 2 shows the main routine for WinJES, which creates the main window,
  25915. and enters the message loop.
  25916.  
  25917.  
  25918. Processing Windows Messages
  25919.  
  25920.  
  25921. In Windows, a window procedure processes Windows messages dispatched from the
  25922. message loop. To create a window procedure for WinJES, I've added processing
  25923. for specific messages that I care about. All other messages go to
  25924. DefWindowProc, the default Windows procedure. The window procedure is shown in
  25925. Listing 3. A description of the critical messages processed follows:
  25926. In the WM_CREATE message:
  25927. open a socket()
  25928. bind() it to an address
  25929. WSAAsyncSelect() defines a message and events to filter
  25930. In the WM_CLOSE or WM_DESTROY message:
  25931. close the socket
  25932. In the defined socket message:
  25933. switch on wParam (a Windows message parameter) to determine the socket
  25934. switch on the low word of lParam (another Windows message parameter) to
  25935. determine the event
  25936. This is a standard model for Windows Sockets servers. WSAAsyncSelect links a
  25937. list of events on a specific socket to a window and establishes the message to
  25938. send to that window when one of the events occurs. When the message is posted,
  25939. the window procedure can look at the low word of lParam to determine the
  25940. specific event that occurred. The specific socket ID is passed as the wParam.
  25941. This scheme allows a single message routine to service multiple requests on
  25942. multiple sockets.
  25943. The window procedure for the WinJES program is very simplistic. The user
  25944. interface is a single-line display, filled from a global variable in response
  25945. to the WM_PAINT message. I've added the menu item to submit commands to other
  25946. WinJES servers to the Windows system menu, so the WinJES applicaton will not
  25947. need a menu of its own.
  25948. From a communications standpoint, the most important message is WM_SOCKET, a
  25949. user-defined message which is defined in the file WINJES.H (on this month's
  25950. code disk). All private messages must be defined above WM_USER (a constant in
  25951. WINDOWS.H). The WM_SOCKET message is referenced in WSAAsyncSelect, so the
  25952. Windows Sockets Dynamic Link Library (DLL) posts it when a socket event
  25953. occurs.
  25954. Because of the Intel architecture, a Windows 3.1 program must deal with a
  25955. two-part memory address: segment and offset. Handling the address correctly is
  25956. critical when passing parameters to code outside the program. All the
  25957. parameters passed from a Windows program to the Windows Sockets DLL routines
  25958. must either be FAR or cast as a FAR when passed. Copying memory also requires
  25959. care. In a UNIX or Windows NT program, the usual way to copy from one address
  25960. to another is memcpy. In Windows 3.1 or Workgroups, programs must use
  25961. _fmemcpy, which is an address-independent version. It will correctly handle
  25962. far pointers.
  25963.  
  25964.  
  25965. WinJES Server Processing
  25966.  
  25967.  
  25968. The server portion of WinJES waits for a message to arrive, then receives and
  25969. parses it. The server extracts the command parameter from the structure, and
  25970. passes it (untouched) to the WinExec API for execution. WinExec takes two
  25971. parameters, a command string and a visibility option. WinJES runs all programs
  25972. as SW_NORMAL, which means they are visible on the screen and receive focus
  25973. when they start. The command string may be a Windows or DOS program name, plus
  25974. any parameters the program needs.
  25975.  
  25976.  
  25977. Preparing a socket for input
  25978.  
  25979.  
  25980. All socket programs begin by allocating a socket for use. A server continues
  25981. by binding the socket to a specific port, and monitoring the port for
  25982. messages. This process occurs in response to a Windows WM_CREATE message. (The
  25983. code is shown under the WM_CREATE case of the window procedure's outermost
  25984. switch statement.) A Windows Sockets program listens by asking the Windows
  25985. Sockets DLL to post a message whenever a defined event occurs. WinJES asks the
  25986. DLL to post a WM_SOCKET message whenever the socket is ready to read. "Ready
  25987. to read" means that the program can perform a receive on the socket and the
  25988. function will not hang. In this UDP program, "ready to read" is the only event
  25989. a server cares about.
  25990.  
  25991.  
  25992.  
  25993. Receiving a message
  25994.  
  25995.  
  25996. When the socket receives data, the Windows Sockets DLL posts a WM_SOCKET
  25997. message, as requested in the WSAAsyncSelect call. (Refer to the WM_SOCKET case
  25998. clause in the window procedure.) The server can look at the long parameter's
  25999. low word to determine the event type. In WinJES, the only critical event is
  26000. FD_READ, which occurs when a request is ready for processing. The parameter
  26001. will match one of the events defined in the WSAAsyncSelect call. Only those
  26002. requested events will trigger the message to the window.
  26003.  
  26004.  
  26005. WinJES Client Processing
  26006.  
  26007.  
  26008. Sending a UDP message is very simple: grab a socket, point it at the remote
  26009. machine's address and the program's port, and toss it out on the network. A
  26010. UDP client will not know if the message arrives unless the server program
  26011. acknowledges it. In WinJES, a UDP message is sent in response to a WM_COMMAND
  26012. message generated by a menu selection. (Refer to case IDM_SEND under case
  26013. WM_COMMAND in the main window procedure.) This code fragment will work for
  26014. many command-line clients, such as a UNIX machine, by moving it and
  26015. recompiling it almost as is (removing Windows-specific calls). The UNIX WinJES
  26016. client would receive the host name as a command-line parameter and use gets to
  26017. read the command to issue, and then runs the (nearly) identical socket code
  26018. for communications.
  26019.  
  26020.  
  26021. Conclusion
  26022.  
  26023.  
  26024. The Windows Sockets interface is a powerful addition to basic Windows
  26025. programming. It extends Windows programs to provide communications over an
  26026. internet easily, while providing a reasonable porting path for TCP/IP code
  26027. from other platforms.
  26028. Understanding Sockets and Protocols
  26029.  
  26030.  
  26031. What are Windows Sockets?
  26032.  
  26033.  
  26034. The Windows Sockets API and interfaces is an open network programming
  26035. interface for Microsoft Windows. The Windows Sockets API is implemented by
  26036. multiple TCP/IP vendors for Windows. Sockets are an abstraction, a method of
  26037. communications between programs using the TCP/IP protocol stack. A socket is a
  26038. bidirectional connection between two programs, which may be running on the
  26039. same or different computers. The Windows Sockets interface is an adaptation of
  26040. Berkeley sockets (as found in many UNIX variants), revised to perform politely
  26041. in a Windows environment. The Windows Sockets dynamic link library (DLL) is
  26042. responsible for notifying a program when a socket needs attention.
  26043.  
  26044.  
  26045. UDP and TCP
  26046.  
  26047.  
  26048. Socket-based programs are asynchronous, and messages can arrive at any time. A
  26049. program defines its own protocol to determine what messages are valid from a
  26050. partner (or proposed partner) at any time.
  26051. Windows Sockets use either TCP (Transmission Control Protocol) or UDP (User
  26052. Datagram Protocol.) TCP provides reliable, two-way, connection-based
  26053. communications, while UDP provides connectionless, "unreliable"
  26054. communications. TCP programs are much like a phone call -- once a connection
  26055. is made, the programs converse, knowing the partner is there. UDP is like
  26056. mailing a letter -- you know the message was sent, but you have no way of
  26057. knowing it arrived (unless your partner sends you another message to confirm.)
  26058. I have always considered the word "unreliable" a bit excessive; on a local
  26059. network, I have never lost a UDP packet.
  26060.  
  26061.  
  26062. TCP/IP Addresses
  26063.  
  26064.  
  26065. Regardless of the protocol used (TCP or UDP), Windows Sockets uses the TCP/IP
  26066. addressing scheme. TCP/IP uses a 32-bit address to identify a machine. The
  26067. machine address is usually expressed in dotted decimal notation, for example,
  26068. my machine address at work is 198.63.66.15. Since multiple programs can
  26069. execute simultaneously on the same machine, each program is assigned a port
  26070. for connections. Some programs (such as mail or file transfers) have
  26071. well-known ports, which are the same on all machines running TCP/IP. Other
  26072. ports are available for user programs. Port numbers below 1,024 are reserved
  26073. for well-known ports. In many UNIX implementations, ports below 4,096 are
  26074. reserved for programs running with root privilege. User-defined ports must be
  26075. at or below port number 65,535. Usually the server will define the port
  26076. number.
  26077. Because people deal with names more easily than numbers, TCP/IP has facilities
  26078. to give machines names as well as numeric addresses. The simplest method is a
  26079. hosts file, which is a list of addresses with the corresponding names. Larger
  26080. installations use DNS (Domain Name Services), which is a centralized method of
  26081. resolving names into valid addresses. A DNS server is a machine on the network
  26082. that runs DNS and responds to name-to-address translation requests.
  26083. WinJES uses the Windows Sockets call gethostbyname to translate names into
  26084. address information. A program can find the name of the machine it's on by
  26085. calling gethostname. Many server programs call gethostname to avoid hardcoding
  26086. the machine name or address in the code. The Windows Sockets library routines
  26087. are usually written to look up names in the local hosts file first, and then
  26088. go to a DNS server to resolve a name. However, different implementations have
  26089. different defaults, so you should check the vendor notes to find the specific
  26090. search order your implementation uses.
  26091.  
  26092.  
  26093. UDP Servers
  26094.  
  26095.  
  26096. A UDP server waits for messages to arrive on a predetermined port, and then
  26097. processes the message. The contents of the message are specific to the server
  26098. program. When a message arrives, the server uses recvfrom to retrieve the
  26099. message and the address of the machine that sent it. To send a reply to the
  26100. client, a UDP server can call sendto, passing the same socket address that the
  26101. recvfrom call returned.
  26102. A UDP server does not usually have a conversation with a client. UDP is a good
  26103. protocol for inbound messages that will cause the server to perform an action.
  26104. Since delivery is not guaranteed, it is possible for a server to miss a
  26105. request. If message loss cannot be tolerated, or you need a two-way
  26106. (request/reply) conversation, use TCP instead.
  26107.  
  26108.  
  26109. TCP Servers
  26110.  
  26111.  
  26112. A TCP server listens for clients to connect to its port. Once a client
  26113. connects, the server program accepts the conversation. The accept call returns
  26114. a socket which the server uses to converse with the client. The server's
  26115. original socket is returned to a listening state for more inbound connections.
  26116. In Windows, a TCP server can create a new window to deal with each inbound
  26117. connection. The new window would deal with the client that connected, and the
  26118. server window would continue to listen for more requests.
  26119.  
  26120.  
  26121. Inter-machine Communications
  26122.  
  26123.  
  26124. Because different machines store integer data differently, the Windows Sockets
  26125. library provides routines to translate from local form to a known format and
  26126. back. Before transferring integers across the network, a program can call
  26127. htons (Host-to-Network-Short) for 16-bit values or htonl
  26128. (Host-To-Network-Long) for 32-bit values to receive a network-neutral value.
  26129. The recepient then calls ntohs or ntohl to translate the data to its local
  26130. form. It is important to know how your C compiler defines an int, a short, and
  26131. a long in bytes, since it varies by platform.
  26132.  
  26133.  
  26134.  
  26135. The WinJES Protocol
  26136.  
  26137.  
  26138. WinJES has a very simple protocol defined on top of UDP datagrams. The
  26139. datagram must contain the following WinJES command structure:
  26140. typedef struct _Message
  26141. {
  26142. char szCommand[MAXBUFFER];
  26143. char szHostName[MAXHOST];
  26144. } Message;
  26145. The WinJES server listens for messages to arrive on UDP port 10000. The port
  26146. number is defined in the WINJES.H file. WinJES servers expect a client to send
  26147. a single datagram to port 10000.
  26148.  
  26149. Listing 1 A UDP server that would work on UNIX systems
  26150. get a socket()
  26151. bind() it to an address
  26152. while ( recvfrom())
  26153. {
  26154. process the incoming message
  26155. }
  26156.  
  26157.  
  26158. Listing 2 The WinJES main program
  26159. #define GLOBALS TRUE
  26160. #include "winjes. h"
  26161. #undef GLOBALS
  26162.  
  26163. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
  26164. LPSTR lpCmdLine, int nCmdShow)
  26165. {
  26166. MSG msg;
  26167.  
  26168. if (!hPrevInstance)
  26169. if (!InitApplication(hInstance))
  26170. return (FALSE);
  26171.  
  26172. if (!InitInstance(hInstance, nCmdShow))
  26173. return (FALSE);
  26174. while (GetMessage(&msg, NULL, NULL, NULL))
  26175. {
  26176. TranslateMessage(&msg);
  26177. DispatchMessage(&msg);
  26178. }
  26179.  
  26180. return (msg.wParam);
  26181. }
  26182.  
  26183. BOOL InitApplication(hInstance)
  26184. HANDLE hInstance;
  26185. {
  26186. WNDCLASS wc;
  26187.  
  26188. wc.style = CS_DBLCLKS;
  26189. wc.lpfnWndProc = MainWndProc;
  26190. wc.cbClsExtra = 0;
  26191. wc.cbWndExtra = 0;
  26192. wc.hInstance = hInstance;
  26193. wc.hIcon = LoadIcon(hInstance, "TellMe");
  26194. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  26195.  
  26196. wc.hbrBackground = GetStockObject(WHITE_BRUSH);
  26197. wc.lpszMenuName = NULL;
  26198. wc.lpszClassName = "jesWClass";
  26199.  
  26200. return (RegisterClass(&wc));
  26201.  
  26202. }
  26203.  
  26204. BOOL InitInstance(HANDLE hInstance, int nCmdShow)
  26205. {
  26206. HWND hWnd;
  26207.  
  26208. hInst = hInstance;
  26209.  
  26210. hWnd = CreateWindow("jesWClass",
  26211. "WinJES",
  26212. WS_OVERLAPPED WS_CAPTION 
  26213. WS_SYSMENU WS_MINIMIZEBOX,
  26214. CW_USEDEFAULT,
  26215. CW_USEDEFAULT,
  26216. 70,
  26217. 50,
  26218. NULL,
  26219. NULL,
  26220. hInstance,
  26221. NULL);
  26222.  
  26223. if (!hWnd)
  26224. return (FALSE);
  26225.  
  26226. ShowWindow(hWnd, nCmdShow);
  26227. UpdateWindow(hWnd);
  26228. return (TRUE);
  26229.  
  26230. }
  26231.  
  26232. /* End of File */
  26233.  
  26234.  
  26235. Listing 3 The WinJES window procedure
  26236. #undef GLOBALS
  26237. #include "winjes.h"
  26238. /*
  26239. Routine: MainWndProc
  26240. Called By: Windows
  26241. Usage: This is the window procedure for the main window
  26242. */
  26243.  
  26244. long FAR PASCAL MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
  26245. lParam)
  26246. {
  26247.  
  26248. BOOL rc;
  26249. char szName[MAXNAME+1];
  26250. FARPROC lpProc;
  26251. HDC hDC;
  26252. HMENU hMenu;
  26253. int n;
  26254. int len;
  26255. int s;
  26256.  
  26257. PAINTSTRUCT ps;
  26258. POINT ptClick;
  26259. RECT rect;
  26260. struct hostent FAR *hp;
  26261. struct sockaddr_in sin;
  26262. WORD wVer;
  26263. WSADATA wsData;
  26264. switch (message)
  26265. {
  26266. case WM_CREATE:
  26267. wVer = 0x101; /* version 1.1 */
  26268. rc = WSAStartup(wVer, (LPWSADATA)&wsData);
  26269. sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  26270. gethostname(szName, MAXNAME);
  26271. hp = gethostbyname((LPSTR)szName);
  26272. if (hp == NULL)
  26273. lstrcpy(szMsg, (LPSTR)"error");
  26274. else
  26275. {
  26276. sin.sin_family = AF_INET;
  26277. sin.sin_port = htons(MYPORT);
  26278. _fmemcpy((struct sockaddr_in FAR *)&sin.sin_addr,
  26279. (struct hostent FAR *)hp->h_addr, hp->h_length);
  26280. n = bind(sock, (struct sockaddr FAR *)&sin, sizeof(sin));
  26281. if (n < 0)
  26282. wsprintf((LPSTR)szMsg, (LPSTR)"bind: %d", n);
  26283. else
  26284. {
  26285. WSAAsyncSelect(sock, hWnd, WM_SOCKET, FD_READ FD_CONNECT FD_CLOSE);
  26286. lstrcpy(szMsg, (LPSTR)"ready");
  26287. }
  26288. }
  26289. InvalidateRect(hWnd, NULL, TRUE);
  26290. /*
  26291. add Tell... to the system menu (so we can get to it even when minimized)
  26292. */
  26293. hMenu = GetSystemMenu(hWnd, FALSE);
  26294. InsertMenu(hMenu, 0, MF_BYPOSITION, IDM_SEND, (LPSTR)"&Sub...");
  26295. InsertMenu(hMenu, 1, MF_BYPOSITION MF_SEPARATOR, (UINT)-1, (LPSTR)"");
  26296. break;
  26297.  
  26298. case WM_SOCKET:
  26299. switch (LOWORD(lParam))
  26300. {
  26301. case FD_READ: // new data/receive queue full
  26302. len = sizeof(sin);
  26303. n = recvfrom(sock, (LPSTR)(Message far *)&InMessage, sizeof(Message), 0,
  26304. (struct sockaddr FAR *)&sin, (int FAR *)&len);
  26305. if (n < 0)
  26306. wsprintf(szMsg, (LPSTR)"err: %d", n);
  26307. else
  26308. {
  26309. if (!lstrcmpi((LPSTR)InMessage.szCommand, (LPSTR)"reboot"))
  26310. ExitWindows(EW_REBOOTSYSTEM, 0);
  26311. if (n = WinExec((LPCSTR)InMessage.szCommand, SW_NORMAL) < 32)
  26312. wsprintf(szMsg, (LPSTR)"rc = %d", n);
  26313. else
  26314. lstrcpy(szMsg, (LPSTR)"command");
  26315. }
  26316.  
  26317. break;
  26318.  
  26319. case FD_CLOSE: // connection closed
  26320. lstrcpy(szMsg, (LPSTR)"closed");
  26321. break;
  26322.  
  26323. case FD_CONNECT: // connection request
  26324. lstrcpy(szMsg, (LPSTR)"connect");
  26325. break;
  26326. }
  26327. InvalidateRect(hWnd, NULL, TRUE);
  26328. break;
  26329.  
  26330. case WM_KEYDOWN:
  26331. if (wParam == VK_HOME)
  26332. PostMessage(hWnd, WM_RBUTTONDOWN, (WPARAM)0, ,MAKELPARAM(0, 0));
  26333. break;
  26334.  
  26335. case WM_RBUTTONDOWN:
  26336. hMenu = CreatePopupMenu();
  26337. AppendMenu(hMenu, MF_STRING, IDM_SEND, (LPSTR)"&Sub...");
  26338. AppendMenu(hMenu, MF_SEPARATOR, (UINT)-1, (LPSTR)"");
  26339. AppendMenu(hMenu, MF_STRING, IDM_ABOUT, (LPSTR)"&About");
  26340. ptClick = MAKEPOINT(lParam);
  26341. ClientToScreen(hWnd, &ptClick);
  26342. TrackPopupMenu(hMenu, TPM_LEFTALIGN, ptClick.x, ptClick.y, 0, hWnd, NULL);
  26343. InvalidateRect(hWnd, NULL, TRUE);
  26344. DestroyMenu(hMenu);
  26345. break;
  26346.  
  26347. case WM_COMMAND:
  26348. switch(wParam)
  26349. {
  26350. case IDM_SEND:
  26351. lpProc = MakeProcInstance(HostProc, hInst);
  26352. rc = DialogBox(hInst, (LPSTR)"Command", hWnd, lpProc);
  26353. FreeProcInstance(lpProc);
  26354. if (!rc)
  26355. break;
  26356. s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  26357. hp = gethostbyname((LPSTR)OutMessage.szHostName);
  26358. if (hp == NULL)
  26359. {
  26360. lstrcpy(szMsg, (LPSTR)"Invalid Host");
  26361. closesocket(s);
  26362. }
  26363. else
  26364. {
  26365. sin.sin_family = AF_INET;
  26366. sin.sin_port = htons(MYPORT);
  26367. _fmemcpy((struct sockaddr_in FAR *)&sin.sin_addr,
  26368. (struct hostent FAR *)hp->h_addr, hp->h_length);
  26369. gethostname((LPSTR)OutMessage.szHostName, MAXHOST-1);
  26370. sendto(s, (LPSTR)(Message far *)&OutMessage, sizeof(Message), 0,
  26371. (struct sockaddr FAR *)&sin, sizeof(sin));
  26372. closesocket(s);
  26373. lstrcpy((LPSTR)szMsg, (LPSTR)"outgoing");
  26374. }
  26375. InvalidateRect(hWnd, NULL, TRUE);
  26376.  
  26377. break;
  26378.  
  26379. case IDM_ABOUT:
  26380. lpProc = MakeProcInstance(AboutProc, hInst);
  26381. rc = DialogBox(hInst, (LPSTR)"About", hWnd, lpProc);
  26382. FreeProcInstance(lpProc);
  26383. break;
  26384. }
  26385. break;
  26386.  
  26387. case WM_SYSCOMMAND:
  26388. switch(wParam)
  26389. {
  26390. case IDM_SEND:
  26391. PostMessage(hWnd, WM_COMMAND, IDM_SEND, 0L);
  26392. break;
  26393.  
  26394. default:
  26395. return (DefWindowProc(hWnd, message, wParam, lParam));
  26396. break;
  26397. }
  26398. break;
  26399.  
  26400. case WM_PAINT:
  26401. hDC = BeginPaint(hWnd, &ps);
  26402. GetClientRect(hWnd, &rect);
  26403. DrawText(hDC, (LPSTR)szMsg, -1, &rect, DT_SINGLELINE DT_CENTER DT_VCENTER);
  26404. EndPaint(hWnd, &ps);
  26405. break;
  26406.  
  26407. case WM_CLOSE:
  26408. DestroyWindow(hWnd);
  26409. break;
  26410.  
  26411. case WM_DESTROY:
  26412. closesocket(sock);
  26413. WSACleanup();
  26414. PostQuitMessage(0);
  26415. break;
  26416.  
  26417. default:
  26418. return (DefWindowProc(hWnd, message, wParam, lParam));
  26419. break;
  26420. }
  26421.  
  26422. return FALSE;
  26423. }
  26424.  
  26425. /* End of File */
  26426.  
  26427.  
  26428.  
  26429.  
  26430.  
  26431.  
  26432.  
  26433.  
  26434.  
  26435.  
  26436.  
  26437.  
  26438.  
  26439.  
  26440.  
  26441.  
  26442.  
  26443.  
  26444.  
  26445.  
  26446.  
  26447.  
  26448.  
  26449.  
  26450.  
  26451.  
  26452.  
  26453.  
  26454.  
  26455.  
  26456.  
  26457.  
  26458.  
  26459.  
  26460.  
  26461.  
  26462.  
  26463.  
  26464.  
  26465.  
  26466.  
  26467.  
  26468.  
  26469.  
  26470.  
  26471.  
  26472.  
  26473.  
  26474.  
  26475.  
  26476.  
  26477.  
  26478.  
  26479.  
  26480.  
  26481.  
  26482.  
  26483.  
  26484.  
  26485.  
  26486.  
  26487.  
  26488.  
  26489.  
  26490.  
  26491.  
  26492.  
  26493.  
  26494.  
  26495.  
  26496.  
  26497.  
  26498.  
  26499.  
  26500. Dynamic Client/Server-Based Image Processing
  26501.  
  26502.  
  26503. Paul Colton
  26504.  
  26505.  
  26506. Paul Colton is a Software Engineer for Inter-National Research Instititute,
  26507. Inc. in San Diego, California. He has over five years programming experience
  26508. and enjoys developing computer graphics software including 3-D rendering
  26509. systems. He runs an Internet-based publishing and software distribution
  26510. company, MiNET, at http://www.minet.com/minit/. Paul can be reached via e-mail
  26511. at pc@minet. com.
  26512.  
  26513.  
  26514.  
  26515.  
  26516. Introduction
  26517.  
  26518.  
  26519. Most imaging systems need to perform complex CPU-intensive image processing
  26520. functions on data. These computations tax the software a great deal and often
  26521. result in the decrease of overall software performance. As a remedy, a
  26522. client/server model is useful in distributing the work.
  26523. In the client/server model with Remote Procedure Calling (RPC), imaging
  26524. programs can be divided into two sections, the client and the server. The
  26525. client takes care of the users' needs, the user interface, file I/O, and any
  26526. other user-specific items. The server performs the image processing
  26527. operations. This division of labor lets the client CPU focus on the user while
  26528. the server CPU focuses on the actual work. Multiple clients can connect to the
  26529. same server, making each of the clients smaller in code size. Because the
  26530. clients and server need not run on the same hardware, the server can benefit
  26531. from a dedicated machine. If the server is running on a much faster platform
  26532. than the clients', each client will attain most of the image processing speed
  26533. of the server to which it is connected. Also, this configuration allows for
  26534. file sharing of data between the clients.
  26535. This article describes the implementation of such a client/server-based image
  26536. processing system. Furthermore, this article shows how to establish a dynamic
  26537. relationship between the client and the server, and how to design the server
  26538. for maximum expandability.
  26539.  
  26540.  
  26541. The Client/Server Model and RPC
  26542.  
  26543.  
  26544. The client/server model for computing is a popular method for distributed
  26545. processing. This model allows processes to make requests for a task while a
  26546. separate process actually does the work. Interprocess communication (IPC) is
  26547. an essential part of the client/server model. IPC allows two processes to
  26548. communicate and exchange data. Unfortunately, IPC is difficult to develop,
  26549. debug, and use. Also, it tends to be machine-dependent.
  26550. Remote Procedure Calling (RPC) was developed so programmers would not have to
  26551. work at this level of complexity. RPC hides the details of IPC and makes the
  26552. development of applications much easier as well as very portable. RPC allows
  26553. local client applications to execute procedures on remote servers. Then the
  26554. servers can send the results of the procedure back to the client.
  26555.  
  26556.  
  26557. The Image Processing Server (IPS)
  26558.  
  26559.  
  26560. The dynamics of the IPS are rooted in the server application. The IPS has
  26561. three main functions:
  26562. 1. To receive an image from a client and decode it into a native format
  26563. 2. To perform image processing functions on that native format
  26564. 3. To return the image to the client in a specified format
  26565. The server does not do the decoding, image processing, and encoding directly,
  26566. but calls upon three banks of supporting programs: loaders, savers, and
  26567. operators. These programs are separate, self-contained executables that
  26568. respectively load, save, and perform operations on images. The server source
  26569. code does not contain any specific information about which image formats are
  26570. supported directly. The source code only knows where the loaders, savers, and
  26571. operators are physically located. The client is responsible for telling the
  26572. server the format of the incoming data. The server will execute the proper
  26573. image loader, if available, to convert the image into an internal format. This
  26574. protocol also applies to the savers and operators.
  26575.  
  26576.  
  26577. The Image-Processing Client
  26578.  
  26579.  
  26580. The image-processing client is responsible for sending the image data to the
  26581. server along with the image format. For example, if the client sends an
  26582. X-Window Dump (XWD) image to the server, the server then checks if the XWD
  26583. loader exists. If so, the server executes the XWD loader using the client file
  26584. as the input parameter and a temporary file as the output parameter. The
  26585. server gives a unique id to the new converted image and returns that id to the
  26586. client. When the client needs to perform image-processing operations on this
  26587. image, the client need only send the image-processing command and the image id
  26588. to the server. When the client has completed sending image-processing
  26589. operations to the server, it can request that the image be sent back in its
  26590. final form. Again, the client sends an image format, and the server executes
  26591. the proper image saver.
  26592.  
  26593.  
  26594. Dynamic Communications
  26595.  
  26596.  
  26597. Neither the server nor the client knows which loaders, savers, or operators
  26598. are available. That is, the source code contains no references to any image
  26599. formats or image-processing functions. Here is where the dynamics of this
  26600. system come into play. The system remains very open, indeed almost infinitely
  26601. expandable, by introducing only new loaders, savers, and operators, and by
  26602. never modifying the original server source code.
  26603. The client and server can communicate with each other to determine what is
  26604. available. The client asks the server for three lists: the loaders available,
  26605. the savers available, and the operators available. The server then checks its
  26606. predetermined loaders, savers, and operators directories and responds with the
  26607. lists. Now the client is fully aware of what image formats and operators are
  26608. available and can make this information available to the user for selection.
  26609. Furthermore, a unique type of loader called an AUTO-DETECT image loader can be
  26610. written. This executable does not load an image directly, but auto-detects the
  26611. image format and then call the proper loader, freeing the client from knowing
  26612. the format of the image, and consequently letting the client operate without
  26613. having to know what loaders are available from the server.
  26614.  
  26615.  
  26616. Implementation
  26617.  
  26618.  
  26619. Developing any RPC applications first requires a communications protocol file.
  26620. This file is written in the ONC RPC Language (RPCL), a C-style language that
  26621. details how the client and server will exchange data. A protocol compiler
  26622. inputs this file and generates the client and server stubs in C. I use the ONC
  26623. RPCGEN protocol compiler. Listing 1 shows the protocol for the Image
  26624. Processing Server, or IPS. To compile this protocol, use the following
  26625. command:
  26626. prompt> rpcgen ips.x
  26627. RPCGEN will generate the client stub, ips_clnt.c, and the server stub,
  26628. ips_svc.c. It will also produce an XDR filter file, ips_xdr.c, as well as a
  26629. header file, ips.h. To compile all of the pieces use the following commands:
  26630. To compile the client stub:
  26631.  
  26632. prompt> cc -c ips_clnt.c
  26633. To compile the server stub:
  26634. prompt> cc -c ips_svc.c
  26635. To compile the XDR filters:
  26636. prompt> cc -c ips_xdr.c
  26637. You should place all of these files in one directory. I chose the name ips as
  26638. my directory name.
  26639. Now that the RPC stubs have been generated and compiled, you must create and
  26640. compile the actual client and server applications, and put all of the pieces
  26641. together. The example server in this article allows clients to perform several
  26642. functions.
  26643. The server must first allow clients to connect to it. Upon connection, the
  26644. server will return a welcome message to the client. Now the client can request
  26645. the lists of loaders, savers, and operators, and it can also send and receive
  26646. images from the server. When the server receives an image file, it returns a
  26647. unique id to the client. The client uses this id to access the image for
  26648. image-processing operations as well as to receive the image back upon
  26649. completion of the processing functions.
  26650. The main function in the server source code is ips_1. This function has a stub
  26651. in the client source code. When the client sends a request to the server via
  26652. ips_1, the proper functions are called via a switch statement in the server
  26653. code. Listing 2 shows the source code for the server process. To compile this
  26654. program, use the following command (broken to fit column):
  26655. prompt> cc -o ips ips.o ips_svc.o
  26656. ips_xdr.o
  26657. The server also requires that a few environment variables be set. These
  26658. environment variables tell the server where the loaders, savers, and operators
  26659. are located, as well as what directory to use for a temporary storage area.
  26660. You must set the following case-sensitive variables prior to running any
  26661. portion of this software:
  26662. LOADERS_DIR -- the absolute path name of the loaders directory
  26663. SAVERS_DIR -- the absolute path name of the savers directory
  26664. OPS_DIR -- the absolute path name of the operators directory
  26665. STORAGE_DIR -- the absolute path name of the temporary storage directory
  26666. I created four directories at the same level as the above-mentioned ips
  26667. directory; they are loaders, savers, operators, and temp. You must set all of
  26668. the above environment variables to these directories. To run the server,
  26669. simply type the name of the server program -- in the case ips. If rou are
  26670. using X-Window, you can run the server in its own window. For example, you
  26671. might use:
  26672. prompt> xterm -e ips &
  26673. This would allow you to view the progress of the server without its output
  26674. interfering in your window. If you're running the server on a single window
  26675. system, you can redirect the server's output to a file or to a null device,
  26676. such as /dev/null.
  26677. Listing 3 shows the client program. This program shows only one example of
  26678. what can be done and does not exhaust all possibilities. For the sake of
  26679. clarity and size, I use no graphic interface, but a simple ASCII menuing
  26680. system. This program allows a user to send an image, receive an image, get the
  26681. list of loaders, savers, and operators, and execute an operator on the current
  26682. image in the server. The server is not limited to storing a single image, but
  26683. this client only demonstrates the use of a single image at a time.
  26684. When you execute the client, it will display the welcome message from the
  26685. server and then proceed to prompt you for input. Entering a question mark will
  26686. display a menu of all available options. When you send image-format type
  26687. information or an operator name, be sure to type it exactly as it is shown
  26688. from the list of available items. (Don't type "xwd" for "XWD".) To compile the
  26689. demo client program, use the following command (broken to fit column):
  26690. prompt> cc -o demo_client demo_client.o
  26691. ips_clnt.o ips_xdr.o
  26692. This client program should reside in the same directory as the server. To run
  26693. the program, make sure the server is already running, then just type
  26694. demo_client followed by the hostname of the machine that is running the
  26695. server.
  26696. Finally, you will need the supporting programs -- the loaders, savers, and
  26697. operators. The loaders, savers, and operators need to agree on an internal
  26698. format, but since they are the only ones using this format, it can be changed
  26699. at any time without recompiling the server or client. As a further advantage,
  26700. the clients and server can be running while you are adding or changing these
  26701. programs. In this article, the internal format is simply implemented as a
  26702. header containing any needed information like number of colors and image size,
  26703. followed by the actual uncompressed image data. Listing 4 shows the header for
  26704. the internal format. All of the supporting programs use this header.
  26705. The loader, saver, and operator all use a small library to keep the code size
  26706. short. This library (Listing 5) contains some convenience routines for loading
  26707. the image data and writing the image data back to a file. The library should
  26708. be linked with the loader, saver, and operator code. To compile the library,
  26709. use the following commands:
  26710. prompt> cc -c fileio.c
  26711. prompt> ar rc libips.a fileio.o
  26712. prompt> ranlib libips.a (for SunOS)
  26713. I have supplied two example loaders for this article, XWD and AUTO-DETECT.
  26714. Listing 6 shows the X-Window (XWD) image-format loader. You should put XWD in
  26715. a different directory than the ips directory, such as loaders_source, along
  26716. with the ips_header.h file. To compile this program, use the following
  26717. command:
  26718. prompt> cc -o XWD xwd.c libips.a
  26719. Move the compiled version of this program into the same directory pointed to
  26720. by the LOADERS_DIR environment variable.
  26721. Listing 7 shows source code for the second loader, AUTO-DETECT. AUTO-DETECT
  26722. demonstrates the use of an auto-detect file format. It supports the detection
  26723. of XWD and GIF images. Put this source code in the same directory as the XWD
  26724. loader source. To compile this program, use the following command:
  26725. prompt> cc -o AUTO-DETECT auto-detect.c
  26726. Move the compiled version of this loader into the LOADERS_DIR directory.
  26727. Listing 8 shows an example file saver for XWD format. To compile the source
  26728. code, follow the same instructions that you used to compile the XWD loader.
  26729. Place this source in a savers_source directory, and then move the executable,
  26730. named XWD, to the same directory as indicated by the SAVERS_DIR environment
  26731. variable. (Note that the XWD loader and saver executables will have the same
  26732. filenames.)
  26733. I include here the source code for one example image operator. Listing 9 shows
  26734. the operator called FLIP-Y. This operator will flip an image vertically. Place
  26735. the source in a operators_source directory. This source can be compiled like
  26736. the others, but you should call the executable FLIP-Y and place it in the same
  26737. directory pointed to by the OPS_DIR environment variable.
  26738. You now have all of the pieces you need to run both the image processing
  26739. server and the demo client. As a test of the system, grab an X-Window Dump
  26740. image via the xwd program and try sending it to the server and getting it
  26741. back. If the server is not responding, make sure all of the above steps have
  26742. been executed properly. Be sure that the image filename you supply to the
  26743. client does not contain any directory information. The file should be located
  26744. in the current directory.
  26745.  
  26746.  
  26747. Extending the Model
  26748.  
  26749.  
  26750. I've shortened the code listed in this article for clarity, but many options
  26751. can be added to the server for even greater functionality. One issue I haven't
  26752. addressed is how to handle loaders, savers, or operators that need more
  26753. information. For example, a SCALE function would need to know the new size of
  26754. the image. One approach is to develop a protocol between the client, server,
  26755. and the loaders, savers, or operators. This protocol would allow the client to
  26756. ask the server what arguments are needed by the program in question; then the
  26757. client could prompt the user for this information. The client would pass the
  26758. final command to the server with the arguments included.
  26759. The server included in this article already allows for arguments to be passed
  26760. to the savers and the operators. This option is useful for savers like JPEG,
  26761. which requires that the user enter a compression ratio.
  26762. The AUTO-DETECT loader can easily be expanded to include new file formats.
  26763. This system need not be limited to imagery. In fact, the server can handle any
  26764. bit-oriented data. The server is indifferent to the data coming across; it
  26765. only passes the data along to the supporting programs. You can easily add
  26766. digital audio, video, and other formats to this system.
  26767.  
  26768.  
  26769. Conclusion
  26770.  
  26771.  
  26772. This dynamic model of a client/server system for image processing opens many
  26773. possibilities: having slower systems harness the power of a faster system on a
  26774. network, sharing files among clients on the network, distributing the image
  26775. processing among multiple servers for improving performance, allowing the
  26776. clients to concentrate the CPU on the user interface, and allowing multiple
  26777. platforms to share the image-processing power of a single server. With some
  26778. creativity, this system can become a very powerful and valuable tool in image
  26779. processing and data handling in general.
  26780.  
  26781.  
  26782. Bibliography
  26783.  
  26784.  
  26785. Bloomer, J. Power Programming with RPC. O'Reilly & Associates, 1991.
  26786.  
  26787.  
  26788. Listing 1 Image processing server (IPS) protocol
  26789. /*
  26790. * Listing 1 - ips.x
  26791. */
  26792.  
  26793. /**** Client/Server Messages ****/
  26794.  
  26795. const IPS_DECODE = 0;
  26796. const IPS_OK_ID = 1;
  26797. const IPS_UNSUPPORTED_FORMAT = 2;
  26798. const IPS_UNKNOWN_FORMAT = 3;
  26799.  
  26800. const IPS_REQUEST_OPS = 4;
  26801. const IPS_SEND_OPS = 5;
  26802.  
  26803. const IPS_REQUEST_LOADERS = 6;
  26804. const IPS_SEND_LOADERS = 7;
  26805.  
  26806. const IPS_REQUEST_SAVERS = 8;
  26807. const IPS_SEND_SAVERS = 9;
  26808.  
  26809. const IPS_REQUEST_IMAGE = 10;
  26810. const IPS_SEND_IMAGE = 11;
  26811.  
  26812. const IPS_PROCESS = 12;
  26813. const IPS_PROCESS_OK = 13;
  26814.  
  26815. const IPS_IMAGE_RELEASE = 14;
  26816. const IPS_RELEASE_OK = 15;
  26817. const IPS_IMAGE_NOT_AVAIL = 16;
  26818.  
  26819. const IPS_INIT = 17;
  26820. const IPS_HELLO = 18;
  26821.  
  26822. const ERROR = 255;
  26823.  
  26824. /******** Image/Text Sizes ********/
  26825.  
  26826. const MAXPIX = 4194304;
  26827.  
  26828. /********** Structures ************/
  26829.  
  26830. struct raw_data {
  26831. string filename<>;
  26832. string format<>;
  26833. opaque data<MAXPIX>;
  26834. };
  26835.  
  26836. struct converted_data {
  26837. opaque data<MAXPIX>;
  26838. };
  26839.  
  26840. struct list {
  26841. string name<>;
  26842. struct list *next;
  26843. };
  26844.  
  26845. struct proc_data {
  26846. int id;
  26847.  
  26848. string command<>;
  26849. int argc;
  26850. list argy;
  26851. };
  26852.  
  26853. struct request_send {
  26854. int id;
  26855. string format<>;
  26856. int argc;
  26857. list argv;
  26858. };
  26859.  
  26860. /********* Main Stuff ***********/
  26861.  
  26862. union Packet switch (int op) {
  26863. case IPS_INIT: void;
  26864. case IPS_HELLO: string hello_string<>;
  26865.  
  26866. case IPS_DECODE: raw_data raw_img;
  26867. case IPS_OK_ID: int id;
  26868. case IPS_UNSUPPORTED_FORMAT: void;
  26869. case IPS_UNKNOWN_FORMAT: void;
  26870.  
  26871. case IPS_REQUEST_OPS: void;
  26872. case IPS_SEND_OPS: list operators;
  26873.  
  26874. case IPS_REQUEST_LOADERS: void;
  26875. case IPS_SEND_LOADERS: list loaders;
  26876.  
  26877. case IPS_REQUEST_SAVERS: void;
  26878. case IPS_SEND_SAVERS: list savers;
  26879.  
  26880. case IPS_REQUEST_IMAGE: request_send send_ops;
  26881. case IPS_SEND_IMAGE: converted_data img;
  26882.  
  26883. case IPS_PROCESS: proc_data proc;
  26884. case IPS_PROCESS_OK: void;
  26885.  
  26886. case IPS_IMAGE_RELEASE: int place_holder;
  26887. case IPS_RELEASE_OK: void;
  26888. case IPS_IMAGE_NOT_AVAIL: void;
  26889.  
  26890. case ERROR: void;
  26891. default: void;
  26892. };
  26893.  
  26894. program IPSPROG {
  26895. version IPSVERS {
  26896. Packet IPS(Packet) = 1;
  26897. } = 1;
  26898. } = 0x20000001;
  26899. /* End of File */
  26900.  
  26901.  
  26902. Listing 2 The server process source code
  26903. /*
  26904. * Listing 2 - ips.c
  26905. */
  26906.  
  26907.  
  26908. #include <stdio.h>
  26909. #include <string.h>
  26910. #include <rpc/rpc.h>
  26911. #include <sys/dir.h>
  26912. #include <unistd.h>
  26913. #include "ips.h"
  26914.  
  26915. /* Constants */
  26916. #define STORAGE_DIR getenv("STORAGE_DIR")
  26917. #define LOADERS_DIR getenv("LOADERS_DIR")
  26918. #define SAVERS_DIR getenv("SAVERS_DIR")
  26919. #define OPS_DIR getenv("OPS_DIR")
  26920.  
  26921. /* Prototypes */
  26922. void ips_hello();
  26923. void ips_decode();
  26924. void ips_process();
  26925. void ips_encode();
  26926. void ips_release();
  26927. void ips_request_list();
  26928. void ips_request_args();
  26929. char *replace_ext( );
  26930. char *get_filename();
  26931. char *upcase();
  26932. int get_next_id();
  26933. unsigned char *loadfile();
  26934.  
  26935. Packet *
  26936. ips_1(request)
  26937. Packet *request;
  26938. {
  26939. static Packet reply;
  26940. char sbuf[1024];
  26941.  
  26942. xdr_free(xdr_Packet, reply);
  26943.  
  26944. switch(request->op) {
  26945. case IPS_INIT:
  26946. ips_hello(request, &reply);
  26947. break;
  26948.  
  26949. case IPS_DECODE:
  26950. ips_decode(request, &reply);
  26951. break;
  26952.  
  26953. case IPS_REQUEST_IMAGE:
  26954. ips_encode(request, &reply);
  26955. break;
  26956.  
  26957. case IPS_PROCESS:
  26958. ips_process(request, &reply);
  26959. break;
  26960.  
  26961. case IPS_IMAGE_RELEASE:
  26962. ips_release(request, &reply);
  26963. break;
  26964.  
  26965. case IPS_REQUEST_OPS:
  26966. ips_request_list(request, &reply);
  26967.  
  26968. break;
  26969.  
  26970. case IPS_REQUEST_LOADERS:
  26971. ips_request_list(request, &reply);
  26972. break;
  26973.  
  26974. case IPS_REQUEST_SAVERS:
  26975. ips_request_list(request, &reply);
  26976. break;
  26977.  
  26978. default:
  26979. reply.op = ERROR;
  26980. }
  26981.  
  26982. return &reply;
  26983. }
  26984.  
  26985.  
  26986. void
  26987. ips_release(request, reply)
  26988. Packet *request, *reply;
  26989. {
  26990. char buf[1024];
  26991.  
  26992. sprintf(buf, "rm -f %s/%d:*",
  26993. STORAGE_DIR, request->Packet_u.id);
  26994.  
  26995. system(buf);
  26996.  
  26997. reply->op = IPS_RELEASE_OK;
  26998. }
  26999.  
  27000.  
  27001. void
  27002. ips_hello(request, reply)
  27003. Packet *request, *reply;
  27004. {
  27005. reply->Packet_u.hello_string = "Welcome to IPS.";
  27006. reply->op = IPS_HELLO;
  27007. }
  27008.  
  27009. void
  27010. ips_request_list(request, reply)
  27011. Packet *request, *reply;
  27012. {
  27013. int i,
  27014. found_listings = 0;
  27015. char *temp_name;
  27016. list *cp,
  27017. *nlp = NULL;
  27018. static DIR *dirp;
  27019. struct direct *d;
  27020.  
  27021.  
  27022. printf("IPS_REQUEST received.\n");
  27023.  
  27024. switch(request->op) {
  27025. case IPS_REQUEST_OPS:
  27026. dirp = opendir(OPS_DIR);
  27027.  
  27028. break;
  27029.  
  27030. case IPS_REQUEST_LOADERS:
  27031. dirp = opendir(LOADERS_DIR);
  27032. break;
  27033.  
  27034. case IPS_REQUEST_SAVERS:
  27035. dirp = opendir(SAVERS_DIR);
  27036. break;
  27037. }
  27038.  
  27039. nlp = &reply->Packet_u.operators;
  27040.  
  27041. while(d = readdir(dirp)) {
  27042. temp_name = d->d_name;
  27043.  
  27044. if(strcmp(temp_name, ".") == 0 
  27045. strcmp(temp_name, "..") == 0)
  27046. continue;
  27047.  
  27048. found_listings = 1;
  27049. nlp->name = strdup(temp_name);
  27050. cp = nlp;
  27051. nlp->next = (list *) malloc(sizeof(list));
  27052. nlp = nlp->next;
  27053. }
  27054.  
  27055. closedir(dirp);
  27056.  
  27057. if(found_listings)
  27058. cp->next = nlp = NULL;
  27059. else {
  27060. nlp->name = "NONE_AVAILABLE";
  27061. nlp->next = NULL;
  27062. }
  27063.  
  27064. switch(request->op) {
  27065. case IPS_REQUEST_OPS:
  27066. reply->op = IPS_SEND_OPS;
  27067. break;
  27068.  
  27069. case IPS_REQUEST_LOADERS:
  27070. reply->op = IPS_SEND_LOADERS;
  27071. break;
  27072.  
  27073. case IPS_REQUEST_SAVERS:
  27074. reply->op = IPS_SEND_SAVERS;
  27075. break;
  27076. }
  27077. }
  27078.  
  27079.  
  27080. void
  27081. ips_process(request, reply)
  27082. Packet *request, *reply;
  27083. {
  27084. int i, stat;
  27085. long len;
  27086. char buf[2000];
  27087.  
  27088. char buf2[2020];
  27089. unsigned char *data;
  27090. char *filename = NULL;
  27091.  
  27092.  
  27093. printf("IPS_PROCESS received.\n");
  27094.  
  27095. sprintf(buf, "%s/%s", OPS_DIR,
  27096. request->Packet_u.proc.command);
  27097.  
  27098. filename =
  27099. get_filename(request->Packet_u.send_ops.id);
  27100.  
  27101. if(access(buf, R_OK) != 0 
  27102. strlen(filename) == 0) {
  27103. reply->op = ERROR;
  27104. return;
  27105. }
  27106.  
  27107. sprintf(buf, "%s/%s %s/%s %s/%s",
  27108. OPS_DIR,
  27109. request->Packet_u.proc.command,
  27110. STORAGE_DIR,
  27111. filename,
  27112. STORAGE_DIR,
  27113. filename);
  27114.  
  27115. if(request->Packet_u.proc.argc > 0) {
  27116. list *lp;
  27117.  
  27118. lp = &request->Packet_u.proc.argv;
  27119.  
  27120. for(i=0;i<request->Packet_u.proc.argc &&
  27121. lp != NULL;i++, lp=lp->next) {
  27122. strcat(buf, " ");
  27123. strcat(buf, lp->name);
  27124. }
  27125. }
  27126.  
  27127. if(system(buf) != 0)
  27128. reply->op = ERROR;
  27129. else
  27130. reply->op = IPS_PROCESS_OK;
  27131.  
  27132. return;
  27133. }
  27134.  
  27135.  
  27136. void
  27137. ips_encode(request, reply)
  27138. Packet *request, *reply;
  27139. {
  27140. int i, stat;
  27141. long len;
  27142. char buf[2000];
  27143. char buf2[2020];
  27144. unsigned char *data;
  27145. char *filename = NULL;
  27146.  
  27147.  
  27148.  
  27149. printf("IPS_REQUEST_IMAGE received.\n");
  27150.  
  27151. filename = get_filename(request->Packet_u.send_ops.id);
  27152.  
  27153. if(strlen(filename) == 0)
  27154. reply->op = IPS_IMAGE_NOT_AVAIL;
  27155. else {
  27156. printf("\tSending %s image.\n",
  27157. request->Packet_u.send_ops.format);
  27158.  
  27159. sprintf(buf. "%s/%s", SAVERS_DIR,
  27160. request->Packet_u.send_ops.format);
  27161.  
  27162. if(access(buf, R_OK) != 0) {
  27163. reply->op = IPS_UNSUPPORTED_FORMAT;
  27164. return;
  27165. }
  27166.  
  27167. sprintf(buf, "%s/%s %s/%s %s/%s";
  27168. SAVERS_DIR,
  27169. request->Packet_u.send_ops.format,
  27170. STORAGE_DIR,
  27171. filename,
  27172. STORAGE_DIR,
  27173. replace_ext(filename,
  27174. request->Packet_u.send_ops.format));
  27175.  
  27176. if(request->Packet_u.send_ops.argc > 0) {
  27177. list *lp;
  27178.  
  27179. lp = &request->Packet_u.send_ops.argv;
  27180.  
  27181. for(i=0;
  27182. i<request->Packet_u.send_ops.argc
  27183. && lp != NULL;
  27184. i++, lp=lp->next) {
  27185.  
  27186. strcat(buf, "");
  27187. strcat(buf, lp->name);
  27188. }
  27189. }
  27190.  
  27191. printf("\tConverting IPS to %s...\n",
  27192. request->Packet_u.send_ops.format);
  27193.  
  27194. stat = system(buf);
  27195.  
  27196. if(stat != 0) {
  27197. printf("\tError %d.\n", stat);
  27198. reply->op = ERROR;
  27199.  
  27200. sprintf(buf, "%s/%s",
  27201. STORAGE_DIR, replace_ext(filename,
  27202. request->Packet_u.send_ops.format));
  27203.  
  27204. sprintf(buf2, "rm -f %s", buf);
  27205. system(buf2);
  27206.  
  27207.  
  27208. return;
  27209. }
  27210.  
  27211. printf("\tSuccess.\n");
  27212.  
  27213. sprintf(buf, "%s/%s", STORAGE_DIR,
  27214. replace_ext(filename,
  27215. request->Packet_u,send_ops.format));
  27216.  
  27217. data = loadfile(buf, &len);
  27218.  
  27219. sprintf(buf2, "rm -f %s", buf);
  27220. system(buf2);
  27221.  
  27222. reply->Packet_u.img.data.data_len = len;
  27223. reply->Packet_u.img.data.data_val =
  27224. (char *) data;
  27225. reply->op = IPS_SEND_IMAGE;
  27226. }
  27227. }
  27228.  
  27229.  
  27230. void
  27231. ips_decode(request, reply)
  27232. Packet *request, *reply;
  27233. {
  27234. FILE *fp;
  27235. char buf[256];
  27236. char format[10];
  27237. unsigned char *data;
  27238. unsigned long id;
  27239. int status;
  27240. char new_filename[1024];
  27241.  
  27242.  
  27243. printf("IPS_DECODE received.\n");
  27244.  
  27245. id = get_next_id();
  27246.  
  27247. reply->Packet_u.id = id;
  27248.  
  27249. sprintf(buf, "%s/%ld:%s",
  27250. STORAGE_DIR, id,
  27251. request->Packet_u.raw_img.filename);
  27252.  
  27253. if((fp = fopen(buf, "w")) == NULL) {
  27254. printf("\tError in opening file %s.\n", buf);
  27255. reply->op = ERROR;
  27256. return;
  27257. }
  27258.  
  27259. fwrite(request->Packet_u.raw_img.data.data_val, 1,
  27260. request->Packet_u.raw_img.data.data_len, fp);
  27261. fclose(fp);
  27262.  
  27263. strcpy(new_filename, buf);
  27264. strcpy(format,
  27265. upcase(request->Packet_u.raw_img.fomat));
  27266.  
  27267.  
  27268. if(strcmp(format, "UNKNOWN") == 0) {
  27269. printf("\tUnknown format.\n");
  27270. reply->op = IPS_UNKNOWN_FORMAT;
  27271. return;
  27272. }
  27273.  
  27274. sprintf(buf, "%s/%s", LOADERS_DIR, format);
  27275.  
  27276. if(access(buf, R_OK) == 0) {
  27277. sprintf(buf, "%s/%s %s %s",
  27278. LOADERS_DIR, format, new_filename,
  27279. replace_ext(new_filename, "ips"));
  27280.  
  27281. printf("\tConverting: DATA...%s...", format);
  27282. fflush(stdout);
  27283.  
  27284. status = system(buf);
  27285.  
  27286. if(status == 0) {
  27287. printf("\n\tSuccess.\n");
  27288. sprintf(buf, "rm %s", new_filename);
  27289. system(buf);
  27290. reply->op = IPS_OK_ID;
  27291.  
  27292. } else {
  27293. printf("\tUnsupported format.\n");
  27294. reply->op = IPS_UNSUPPORTED_FORMAT;
  27295. }
  27296.  
  27297. } else {
  27298. printf("\tUnsupported format.\n");
  27299. reply->op = IPS_UNSUPPORTED_FORMAT;
  27300. }
  27301. }
  27302.  
  27303.  
  27304. int
  27305. get_next_id()
  27306. {
  27307. static unsigned long current_id = 0;
  27308. current_id++;
  27309.  
  27310. if(current_id > 999999)
  27311. current_id = 0;
  27312.  
  27313. return current_id;
  27314. }
  27315.  
  27316.  
  27317. char *
  27318. replace_ext(filename, ext)
  27319. char *filename;
  27320. char *ext;
  27321. {
  27322. static char newbuf[1024];
  27323. char temp[1024];
  27324. char *p;
  27325.  
  27326. strcpy(temp, filename);
  27327.  
  27328. p = temp;
  27329.  
  27330. while(*p != '.' && *p ! = 0)
  27331. p++;
  27332.  
  27333. if(*p != 0)
  27334. *p = 0;
  27335.  
  27336. trcpy(newbuf, temp);
  27337. strcat(newbuf, ".");
  27338. strcat(newbuf, ext);
  27339.  
  27340. return newbuf;
  27341. }
  27342.  
  27343.  
  27344. char *
  27345. get_filename(id)
  27346. unsigned long id;
  27347. {
  27348. static DIR *dirp;
  27349. struct direct *d;
  27350. int temp_id;
  27351. static char filename[1024];
  27352. struct filelist_node {
  27353. char *filename;
  27354. struct filelist_node *next;
  27355. };
  27356.  
  27357. struct filelist_node filelist, *clp, *flp;
  27358.  
  27359. dirp = opendir(STORAGE_DIR);
  27360.  
  27361. flp = &filelist;
  27362.  
  27363. while(d = readdir(dirp)) {
  27364. flp->filename = d->d_name;
  27365. flp->next = (struct filelist_node *)
  27366. malloc(sizeof(struct filelist_node));
  27367. clp = flp;
  27368. flp = flp->next;
  27369. }
  27370.  
  27371. closedir(dirp);
  27372.  
  27373. clp->next = NULL;
  27374. filename[0] = NULL;
  27375.  
  27376. for(flp=&filelist;flp != NULL; flp=flp->next) {
  27377. int temp_id;
  27378.  
  27379. if(strcmp(flp->filename, ".") == 0
  27380. strcmp(flp->filename, "..") == 0)
  27381. continue;
  27382.  
  27383. sscanf(flp->filename, "%d:", &temp_id);
  27384.  
  27385. if(temp_id == id)
  27386. strcpy(filename, flp->filename);
  27387.  
  27388. }
  27389.  
  27390. for(flp=&filelist;clp != NULL; flp=clp=flp->next)
  27391. free(flp);
  27392.  
  27393. return filename;
  27394. }
  27395.  
  27396.  
  27397. unsigned char *
  27398. loadfile(filename, len)
  27399. char *filename;
  27400. long *len;
  27401. {
  27402. FILE *fp;
  27403. long size;
  27404. unsigned char *raw_img;
  27405.  
  27406. fp = fopen(filename, "r");
  27407. fseek(fp, 0, 2);
  27408. *len = size = ftell(fp);
  27409.  
  27410. fseek(fp, 0, 0);
  27411.  
  27412. raw_img = (unsigned char *) malloc(size);
  27413. fread(raw_img, 1, size, fp);
  27414. fclose(fp);
  27415.  
  27416. return raw_img;
  27417. }
  27418.  
  27419.  
  27420. char *
  27421. upcase(text)
  27422. char *text;
  27423. {
  27424. char *p;
  27425. static char buf[1000];
  27426.  
  27427. strcpy(buf, text);
  27428. p = buf;
  27429.  
  27430. while(*p != NULL) {
  27431. *p = toupper(*p);
  27432. p++;
  27433. }
  27434.  
  27435. return buf;
  27436. }
  27437. /* End of File */
  27438.  
  27439.  
  27440. Listing 3 The client program
  27441. /*
  27442. * Listing 3 - demo_client.c
  27443. */
  27444.  
  27445. #include <stdio.h>
  27446. #include <rpc/rpc.h>
  27447.  
  27448. #include <malloc.h>
  27449. #include <string.h>
  27450. #include <fcntl.h>
  27451. #include <unistd.h>
  27452. #include <ctype.h>
  27453. #include <sys/time.h>
  27454. #include "ips.h"
  27455.  
  27456. /* Prototypes */
  27457. char *get_file();
  27458. void image_release();
  27459. void upcase();
  27460. void help_text();
  27461.  
  27462. /* Globals */
  27463. static unsigned long image_id=-1;
  27464. static struct timeval Timeout = { 10000, 0 };
  27465.  
  27466. void
  27467. main(argc, argv)
  27468. int argc;
  27469. char **argv;
  27470. {
  27471. int done = 0;
  27472. CLIENT *cl;
  27473. char cmd[256];
  27474. Packet *request, *reply;
  27475. int id;
  27476. char command[256];
  27477.  
  27478. request = (Packet *) malloc(sizeof(Packet));
  27479.  
  27480. if(argc < 2) {
  27481. printf("Usage: %s server\n", argv[0]);
  27482. exit(0);
  27483. }
  27484.  
  27485. if(!(cl = clnt_create(argv[1], IPSPROG, IPSVERS,
  27486. "tcp"))) {
  27487. clnt_pcreateerror(argv[1]);
  27488. exit(1);
  27489. }
  27490.  
  27491. clnt_control(cl, CLSET_TIMEOUT, (char *)&Timeout);
  27492.  
  27493. request->op = IPS_INIT;
  27494. reply = ips_1(request, cl);
  27495.  
  27496. if(reply)
  27497. printf("\n%s\n\n",
  27498. reply->Packet_u.hello_string);
  27499.  
  27500. while(!done) {
  27501. printf("(? for help)>>");
  27502. fflush(stdout);
  27503. gets(cmd);
  27504.  
  27505. if(strcmp(cmd, "help") == 0 
  27506. strcmp(cmd, "?") == 0)
  27507.  
  27508. help_text();
  27509.  
  27510. else if(strcmp(cmd, "send") == 0)
  27511. decode(cl);
  27512.  
  27513. else if(strcmp(cmd, "get") == 0)
  27514. encode(cl);
  27515.  
  27516. else if(strcmp(cmd, "process") == 0)
  27517. process(cl);
  27518.  
  27519. else if(strcmp(cmd, "operators") == 0)
  27520. get_operators(cl);
  27521.  
  27522. else if(strcmp(cmd, "loaders") == 0)
  27523. get_loaders(cl);
  27524.  
  27525. else if(strcmp(cmd, "savers") == 0)
  27526. get_savers(cl);
  27527.  
  27528. else if(strcmp(cmd, "quit") == 0) {
  27529. image_release(cl);
  27530. done = 1;
  27531. }
  27532. printf("\n");
  27533. }
  27534. }
  27535.  
  27536.  
  27537. int
  27538. encode(cl)
  27539. CLIENT *cl;
  27540. {
  27541. char buf[256];
  27542. char filename[256];
  27543. char format[40];
  27544. Packet *reply, *request;
  27545.  
  27546.  
  27547. request = (Packet *) malloc(sizeof(Packet));
  27548.  
  27549. printf("\nIPS Send Image:\n\n");
  27550.  
  27551. printf("Enter image filename (abort to quit): ");
  27552. fflush(stdout);
  27553. gets(filename);
  27554. if(strcmp(filename, "abort") == 0)
  27555. return 1;
  27556.  
  27557. printf("Enter format (abort to quit): ");
  27558. fflush(stdout);
  27559. gets(format);
  27560. if(strcmp(format, "abort") == 0)
  27561. return 1;
  27562.  
  27563. upcase(format);
  27564.  
  27565. request->op = IPS_REQUEST_IMAGE;
  27566. request->Packet_u.send_ops.id = image_id;
  27567.  
  27568. request->Packet_u.send_ops.format = format;
  27569. request->Packet_u.send_ops.argc = 0;
  27570. request->Packet_u.send_ops._argv.name = "test";
  27571. request->Packet_u.send_ops._argv.next = NULL;
  27572.  
  27573. reply = ips_1(request, cl);
  27574.  
  27575. if(!reply) {
  27576. puts("Reply error or timeout.");
  27577. exit(1);
  27578. }
  27579.  
  27580. if(reply->op == IPS_SEND_IMAGE) {
  27581. FILE *fp;
  27582.  
  27583. fp = fopen(filename, "w");
  27584. fwrite(reply->Packet_u.img.data.data_val, 1,
  27585. reply->Packet_u.img.data.data_len, fp);
  27586. fclose(fp);
  27587.  
  27588. printf("\"%s\" Received.\n", filename);
  27589.  
  27590. } else if(reply->op == IPS_IMAGE_NOT_AVAIL)
  27591. printf("IPS_IMAGE_NOT_AVAIL recevied.\n");
  27592.  
  27593. else if(reply->op == IPS_UNSUPPORTED_FORMAT)
  27594. printf("IPS_UNSUPPORTED_FORMAT received\n");
  27595.  
  27596. else {
  27597. printf("Error in encoding.\n");
  27598. return 1;
  27599. }
  27600.  
  27601. free(request);
  27602. if(reply) xdr_free(xdr_Packet, reply);
  27603.  
  27604. return 0;
  27605. }
  27606.  
  27607.  
  27608. int
  27609. decode(cl)
  27610. CLIENT *cl;
  27611. {
  27612. FILE *fp;
  27613. long size;
  27614. char buf[256];
  27615. char buf2[256];
  27616. char *filename;
  27617. unsigned char *raw_img;
  27618. Packet *reply, *request;
  27619.  
  27620.  
  27621. request = (Packet *) malloc(sizeof(Packet));
  27622.  
  27623. if((filename = get_file()) == NULL) {
  27624. printf("Error or aborted.\n");
  27625. return 1;
  27626. }
  27627.  
  27628.  
  27629. printf("Please enter image format: ");
  27630. fflush(stdout);
  27631. scanf("%s", buf2);
  27632.  
  27633. upcase(buf2);
  27634.  
  27635. image_release(cl);
  27636.  
  27637. sprintf(buf, "%s", filename);
  27638.  
  27639. fp = fopen(buf, "r");
  27640. fseek(fp, 0, 2);
  27641. size = ftell(fp);
  27642.  
  27643. fseek(fp, 0, 0);
  27644.  
  27645. raw_img = (unsigned char *) malloc(size);
  27646. fread(raw_img, 1, size, fp);
  27647. fclose(fp);
  27648.  
  27649. request->op = IPS_DECODE;
  27650. request->Packet_u.raw_img.filename = filename;
  27651. request->Packet_u.raw_img.format = strdup(buf2);
  27652. request->Packet_u.raw_img.data.data_len = size;
  27653. request->Packet_u.raw_img.data.data_val =
  27654. (char *) raw_img;
  27655.  
  27656. reply = ips_1(request, cl);
  27657.  
  27658. if(!reply) {
  27659. puts("Timeout.");
  27660. exit(1);
  27661. }
  27662.  
  27663. if(reply->op == IPS_OK_ID) {
  27664. image_id = reply->Packet_u.id;
  27665. } else {
  27666. printf("Error in decoding.\n\n");
  27667. return 1;
  27668. }
  27669.  
  27670. free(raw_img);
  27671. free(request);
  27672. xdr_free(xdr_Packet, reply);
  27673.  
  27674. return 0;
  27675. }
  27676.  
  27677.  
  27678. int
  27679. process(cl)
  27680. CLIENT *cl;
  27681. {
  27682. int i,
  27683. retval;
  27684. list *nlp, *cp;
  27685. char buf[256];
  27686. Packet *reply = NULL,
  27687.  
  27688. *request;
  27689. int nitems;
  27690. char *items[10];
  27691.  
  27692. if(image_id == -1) {
  27693. printf("No image sent to server.\n");
  27694. return;
  27695. }
  27696.  
  27697. /*
  27698. For this article, nitems will be 0, but
  27699. this allows you to send arguments to the
  27700. operator program. For example, if you
  27701. were executing a SCALE program, you would
  27702. have to send the new width and height.
  27703. */
  27704.  
  27705. nitems = 0;
  27706.  
  27707. request = (Packet *) malloc(sizeof(Packet));
  27708.  
  27709. printf("Please enter processing command: ");
  27710. fflush(stdout);
  27711. scanf("%s", buf);
  27712.  
  27713. upcase(buf);
  27714.  
  27715. request->op = IPS_PROCESS;
  27716. request->Packet_u.proc.id = image_id;
  27717. request->Packet_u.proc.command = strdup(buf);
  27718. request->Packet_u.proc.argc = nitems;
  27719.  
  27720. if(nitems == 0) {
  27721. request->Packet_u.proc.argv.name = "\0";
  27722. request->Packet_u.proc.argv.next = 0;
  27723.  
  27724. } else {
  27725. nlp = &request->Packet_u.proc.argv;
  27726. for(i=0;i<nitems;i++) {
  27727. nlp->name = strdup(items[i]);
  27728. cp = nlp;
  27729. nlp->next = (list *) malloc(sizeof(list));
  27730. nlp = nlp->next;
  27731. }
  27732. cp->next = nlp = NULL;
  27733. }
  27734.  
  27735. reply = ips_1(request, cl);
  27736.  
  27737. free(request);
  27738.  
  27739. if(!reply) {
  27740. puts("Encode timeout.");
  27741. return -1;
  27742. }
  27743.  
  27744. if(reply->op == IPS_PROCESS_OK)
  27745. retval = 0;
  27746. else {
  27747.  
  27748. puts("There was an error processing.\n");
  27749. retval = -1;
  27750. }
  27751.  
  27752. xdr_free(xdr_Packet, reply);
  27753.  
  27754. return retval;
  27755. }
  27756.  
  27757.  
  27758. int
  27759. get_loaders(cl)
  27760. CLIENT *cl;
  27761. {
  27762. list *nlp;
  27763. Packet *reply, *request;
  27764.  
  27765.  
  27766. request = (Packet *) malloc(sizeof(Packet));
  27767.  
  27768. request->op = IPS_REQUEST_LOADERS;
  27769. reply = ips_1(request, cl);
  27770.  
  27771. switch(reply->op) {
  27772. case IPS_SEN_LOADERS:
  27773. printf("\nIPS Load Formats:\n\n");
  27774. for(nlp = &reply->Packet_u.operators;
  27775. nlp != NULL;
  27776. nlp = nlp->next)
  27777. printf(" %s\n", nlp->name);
  27778. printf("\n");
  27779. break;
  27780.  
  27781. case ERROR:
  27782. printf("get_loaders failed.\n");
  27783. return 1;
  27784. }
  27785.  
  27786. free(request);
  27787. xdr_free(xdr_Packet, reply);
  27788.  
  27789. return 0;
  27790. }
  27791.  
  27792. int
  27793. get_savers(cl)
  27794. CLIENT *cl;
  27795. {
  27796. list *nlp;
  27797. Packet *reply, *request;
  27798.  
  27799.  
  27800. request = (Packet *) malloc(sizeof(Packet));
  27801.  
  27802. request->op = IPS_REQUEST_SAVERS;
  27803. reply = ips_1(request, cl);
  27804.  
  27805. switch(reply->op) {
  27806. case IPS_SEND_SAVERS:
  27807.  
  27808. printf("\nIPS Save Formats:\n\n");
  27809. for(nlp = &reply->Packet_u.operators;
  27810. nlp != NULL;
  27811. nlp = nlp->next)
  27812. printf(" %s\n", nlp->name);
  27813. printf("\n");
  27814. break;
  27815.  
  27816. case ERROR:
  27817. printft"get_savers failed.\n");
  27818. return 1;
  27819. }
  27820.  
  27821. free(request);
  27822. xdr_free(xdr_Packet, reply);
  27823.  
  27824. return 0;
  27825. }
  27826.  
  27827.  
  27828. int
  27829. get_operators(cl)
  27830. CLIENT *cl;
  27831. {
  27832. list *nlp;
  27833. Packet *reply, *request;
  27834.  
  27835.  
  27836. request = (Packet *) malloc(sizeof(Packet));
  27837.  
  27838. request->op = IPS_REQUEST_OPS;
  27839. reply = ips_1(request, cl];
  27840.  
  27841. switch(reply->op) {
  27842. case IPS_SEND_OPS:
  27843. printf("\nIPS Processing Tools:\n\n");
  27844. for(nlp = &reply->Packet_u.operators;
  27845. nlp != NULL;
  27846. nlp = nlp->next)
  27847. printf(" %s\n", nlp->name);
  27848. printf("\n");
  27849. break;
  27850. case ERROR:
  27851. printf("get_operators failed.\n");
  27852. return 1;
  27853. }
  27854.  
  27855. free(request);
  27856. xdr_free(xdr_Packet, reply);
  27857.  
  27858. return 0;
  27859. }
  27860.  
  27861.  
  27862. char *
  27863. get_file()
  27864. {
  27865. int status;
  27866. char filename[1024], command[1024];
  27867.  
  27868.  
  27869. printf("\nIPS Receive Image:\n\n");
  27870. printf("Type 'abort' to cancel. Filename: ");
  27871. fflush(stdout);
  27872. gets(filename);
  27873.  
  27874. if(strcmp(filename, "abort") == 0)
  27875. return NULL;
  27876.  
  27877. if(access(filename, R_OK) == 0)
  27878. return strdup(filename);
  27879.  
  27880. return NULL;
  27881. }
  27882.  
  27883.  
  27884. void
  27885. image_release(cl)
  27886. CLIENT *cl;
  27887. {
  27888. Packet *request, *reply;
  27889.  
  27890. request = (Packet *) malloc(sizeof(Packet));
  27891.  
  27892. if(image_id != -1) {
  27893. request->op = IPS_IMAGE_RELEASE;
  27894. request->Packet_u.id = image_id;
  27895. reply = ips_1(request, cl);
  27896.  
  27897. if(reply->op == IPS_RELEASE_OK)
  27898. printf("Release of %d ok.\n", image_id);
  27899. }
  27900. free(request);
  27901. }
  27902.  
  27903.  
  27904. void
  27905. upcase(text)
  27906. char *text;
  27907. {
  27908. char *p = text;
  27909.  
  27910. while(*p != NULL) {
  27911. *p = toupper(*p);
  27912. p++;
  27913. }
  27914. }
  27915.  
  27916.  
  27917. void
  27918. help_text()
  27919. {
  27920. printf("\n");
  27921. printf(" Cmd Description\n");
  27922. printf("------- -----------------------\n");
  27923. printf(" send send an image to the IPS\n");
  27924. printf(" get receive an image from IPS\n");
  27925. printf(" in a certain format\n");
  27926. printf(" process process an image\n");
  27927.  
  27928. printf(" loaders list all available loaders\n");
  27929. printf(" savers list all available savers\n");
  27930. printf(" operators list all avail. operators\n");
  27931. printf(" quit exit this client\n");
  27932. printf("\n");
  27933. }
  27934.  
  27935. /* End of File */
  27936.  
  27937.  
  27938. Listing 4 Internal image format
  27939. /*
  27940. * Listing 4 - ips_header.h
  27941. */
  27942.  
  27943. #ifndef _IPS_IMAGE_H
  27944. #define _IPS_IMAGE_H
  27945.  
  27946. #include <X11/Xlib.h>
  27947.  
  27948. typedef struct {
  27949. int cmap_count;
  27950. int bpp;
  27951. int width;
  27952. int height;
  27953. } ips_header;
  27954.  
  27955. typedef struct {
  27956. XColor cmap[256];
  27957. unsigned char *data;
  27958. } ips_image;
  27959.  
  27960. #endif
  27961. /* End of File */
  27962.  
  27963.  
  27964. Listing 5 Small file I/O library for loading and saving images
  27965. /*
  27966. * Listing 5 - fileio.c (libips.a)
  27967. */
  27968.  
  27969. #include <stdio.h>
  27970. #include "ips_image.h"
  27971.  
  27972. int img_to_ips();
  27973. int img_to_ips_fp();
  27974. int ips_to_img();
  27975. int ips_to_img_fp();
  27976.  
  27977.  
  27978. int
  27979. img_to_ips(header, image, filename)
  27980. ips_header *header;
  27981. ips_image *image;
  27982. char *filename;
  27983. {
  27984. int retval;
  27985. FILE *fp;
  27986.  
  27987.  
  27988.  
  27989. if((fp = fopen(filename, "w")) == NULL) {
  27990. perror("img_to_ips");
  27991. return 0;
  27992. }
  27993.  
  27994. retval = img_to_ips_fp(header, image, fp);
  27995.  
  27996. fclose(fp);
  27997.  
  27998. return retval;
  27999. }
  28000.  
  28001. int
  28002. img_to_ips_fp(header, image, fp)
  28003. ips_header *header;
  28004. ips_image *image;
  28005. FILE *fp;
  28006. {
  28007. fwrite(header, sizeof(ips_header), 1, fp);
  28008. fwrite(image->cmap, 1, sizeof(XColor) * header->cmap_count, fp);
  28009. fwrite(image->data, 1, header->width * header->height, fp);
  28010.  
  28011. return 1;
  28012. }
  28013.  
  28014.  
  28015. int
  28016. ips_to_img(header, image, filename)
  28017. ips_header *header;
  28018. ips_image *image;
  28019. char *filename;
  28020. {
  28021. int retval;
  28022. FILE *fp;
  28023.  
  28024.  
  28025. if((fp = fopen(filename, "r")) == NULL) {
  28026. perror("ips_to_img");
  28027. return 0;
  28028. }
  28029.  
  28030. retval = ips_to_img_fp(header, image, fp);
  28031.  
  28032. fclose(fp);
  28033.  
  28034. return retval;
  28035. }
  28036.  
  28037. int
  28038. ips_to_img_fp(header, image, fp)
  28039. ips_header *header;
  28040. ips_image *image;
  28041. FILE *fp;
  28042. {
  28043. fread(header, sizeof(ips_header), 1, fp);
  28044.  
  28045. if(header->bpp != 8) {
  28046. fprintf(stderr,
  28047.  
  28048. "ips_to_img_fp: data is not in 8 bit format.\n");
  28049. return 0;
  28050. }
  28051.  
  28052. image->data = (unsigned char *)
  28053. malloc(header->width * header->height);
  28054.  
  28055. if(image->data == NULL) {
  28056. perror("ips_to_img_fp");
  28057. return 0;
  28058. }
  28059.  
  28060. fread(image->cmap, 1, sizeof(XColor) * header->cmap_count, fp);
  28061. fread(image->data, 1, header->width * header->height, fp);
  28062.  
  28063. return 1;
  28064. }
  28065. /* End of File */
  28066.  
  28067.  
  28068. Listing 6 X-Window Dump (XWD) image format loader
  28069. /*
  28070. * Listing 6 - xwd.c (loader)
  28071. */
  28072.  
  28073. #include <stdio.h>
  28074. #include <math.h>
  28075. #include <X11/XWDFile.h>
  28076. #include "ips_image.h"
  28077.  
  28078. void read_ximage();
  28079.  
  28080. int
  28081. main(argc, argv)
  28082. int argc;
  28083. char **argv;
  28084. {
  28085. ips_header header;
  28086. ips_image image;
  28087.  
  28088. if (argc < 3) {
  28089. printf("Usage: %s infile outfile\n", argv[0]);
  28090. exit(1);
  28091. }
  28092.  
  28093. read_ximage(&header, &image, argv[1]);
  28094.  
  28095. img_to_ips(&header, &image, argv[2]);
  28096.  
  28097. exit(0);
  28098. }
  28099.  
  28100.  
  28101. void
  28102. read_ximage(header, image, filename)
  28103. ips_header *header;
  28104. ips_image *image;
  28105. char *filename;
  28106. {
  28107.  
  28108. int i, pad, rightpad;
  28109. char buf[1024];
  28110. FILE *fp;
  28111. unsigned char *p;
  28112. XWDColor xcmap[256];
  28113. XWDFileHeader xheader;
  28114.  
  28115. if((fp = fopen(filename, "r")) == NULL) {
  28116. perror("read_ximage");
  28117. exit(1);
  28118. }
  28119.  
  28120. fread(&xheader, 1, sizeof(XWDFileHeader), fp);
  28121. fread(buf, 1, (xheader.header_size - sizeof(XWDFileHeader)), fp);
  28122. fread(xcmap, 1, sizeof(XWDColor) * xheader.ncolors, fp);
  28123.  
  28124. p = image->data = (unsigned char *)
  28125. malloc(xheader.pixmap_width * header.pixmap_height);
  28126.  
  28127. rightpad = xheader.bytes_per_line - xheader.pixmap_width;
  28128.  
  28129. for (i=0; i<xheader.pixmap_height; i++) {
  28130. fread((p + (i * xheader.pixmap_width)), 1,
  28131. xheader.pixmap_width, fp);
  28132.  
  28133. for (pad=0; pad < rightpad; pad++)
  28134. fgetc(fp);
  28135. }
  28136.  
  28137. header->bpp = 8;
  28138. header->cmap_count = xheader.ncolors;
  28139. header->width = xheader.pixmap_width;
  28140. header->height = xheader.pixmap_height;
  28141.  
  28142. for(i=0; i < header->cmap_count; i++) {
  28143. image->cmap[i].pixel = i;
  28144. image->cmap[i].red = xcmap[i].red >> 8;
  28145. image->cmap[i].green = xcmap[i].green >> 8;
  28146. image->cmap[i].blue = xcmap[i].blue >> 8;
  28147. }
  28148.  
  28149. fclose(fp);
  28150. }
  28151. /* End of File */
  28152.  
  28153.  
  28154. Listing 7 A loader that auto-detects the image file format
  28155. /*
  28156. * Listing 7 - auto-detect.c
  28157. */
  28158.  
  28159. #include <stdio.h>
  28160. #include <unistd.h>
  28161.  
  28162. int
  28163. main(argc, argv)
  28164. int argc;
  28165. char **argv;
  28166. {
  28167.  
  28168. FILE *fp;
  28169. char buf[1000];
  28170. char buf2[1000];
  28171. char magic_number[12];
  28172. char format[5];
  28173.  
  28174. if(argc < 3) {
  28175. printf("Usage: %s infile outfile\n", argv[0]);
  28176. exit(1);
  28177. }
  28178.  
  28179. fp = fopen(argv[1], "r");
  28180.  
  28181. fread(magic_number, sizeof(char),
  28182. sizeof(magic_number), fp);
  28183.  
  28184. fclose(fp);
  28185.  
  28186. if (strncmp(magic_number,"GIF8",4) == 0)
  28187. (void) strcpy(format,"GIF");
  28188.  
  28189. else if ((magic_number[1] == 0x00) &&
  28190. (magic_number[2] == 0x00)) {
  28191. if ((magic_number[5] == 0x00) &&
  28192. (magic_number[6] == 0x00))
  28193. if ((magic_number[4] == 0x07) 
  28194. (magic_number[7] == 0x07))
  28195. strcpy(format,"XWD");
  28196.  
  28197. } else exit(1);
  28198.  
  28199. if(strlen(format) == 0) exit(1);
  28200.  
  28201. sprintf(buf2, "%s/%s",
  28202. getenv("LOADERS_DIR"), format);
  28203.  
  28204. if(access(buf2, R_OK) != 0) {
  28205. printf("\n%s: %s format not available.\n",
  28206. argv[0], format);
  28207.  
  28208. exit(1);
  28209. }
  28210.  
  28211. sprintf(buf, "%s %s %s", buf2, argv[1], argv[2]);
  28212.  
  28213. return system(buf);
  28214. }
  28215. /* End of File */
  28216.  
  28217.  
  28218. Listing 8 Example XWD format saver
  28219. /*
  28220. * Listing 8 - xwd.c (saver)
  28221. */
  28222.  
  28223. #include <stdio.h>
  28224. #include <stdlib.h>
  28225. #include <math.h>
  28226. #include <X11/XWDFile.h>
  28227.  
  28228. #include "ips_image.h"
  28229.  
  28230. void write_ximage();
  28231.  
  28232. int
  28233. main(argc, argv)
  28234. int argc;
  28235. char **argv;
  28236. {
  28237. ips_header header;
  28238. ips_image image;
  28239.  
  28240. if (argc<3) {
  28241. printf("Usage: %s infile outfile\n", argv[0]);
  28242. exit(1);
  28243. }
  28244.  
  28245. ips_to_img(&header, &image, argv[1]);
  28246.  
  28247. write_ximage(&header, &image, argv[2]);
  28248.  
  28249. exit(0);
  28250. }
  28251.  
  28252.  
  28253. void
  28254. write_ximage(header, image, filename)
  28255. ips_header *header;
  28256. ips_image *image;
  28257. char *filename;
  28258. {
  28259. int i;
  28260. FILE *fp;
  28261. XWDFileHeader xheader;
  28262. XWDColor xcmap[256];
  28263.  
  28264.  
  28265. memset(&xheader, 0, sizeof(XWDFileHeader));
  28266.  
  28267. xheader.header_size = sizeof(XWDFileHeader);
  28268. xheader.file_version = XWD_FILE_VERSION;
  28269. xheader.pixmap_format = 2; /* ZPixmap */
  28270. xheader.pixmap_depth = header->bpp;
  28271. xheader.pixmap_width = header->width;
  28272. xheader.pixmap_height = header->height;
  28273. xheader.byte_order = 1;
  28274. xheader.bitmap_unit = 32;
  28275. xheader.bitmap_bit_order = 1;
  28276. xheader.bitmap_pad = 32;
  28277. xheader.bits_per_pixel = 8;
  28278. xheader.bytes_per_line = header->width;
  28279. xheader.visual_class = 3; /* PseudoColor */
  28280. xheader.bits_per_rgb = header->bpp;
  28281. xheader.colormap_entries = header->cmap_count;
  28282. xheader.ncolors = header->cmap_count;
  28283. xheader.window_width = header->width;
  28284. xheader.window_height = header->height;
  28285.  
  28286. for (i=0; i < header->cmap_count; i++) {
  28287.  
  28288. xcmap[i].pixel = i;
  28289. xcmap[i].red = image->cmap[i].red << 8;
  28290. xcmap[i].green = image->cmap[i].green << 8;
  28291. xcmap[i].blue = image->cmap[i].blue << 8;
  28292. }
  28293.  
  28294. if ((fp = fopen(filename, "w")) == NULL) {
  28295. perror("write_ximage");
  28296. exit(1);
  28297. }
  28298.  
  28299. fwrite(&xheader, 1, sizeof(XWDFileHeader), fp);
  28300. fwrite(xcmap, 1, sizeof(XWDColor) * header->cmap_count, fp);
  28301. fwrite(image->data, 1, header->width * header->height, fp);
  28302.  
  28303. fclose(fp);
  28304. }
  28305. /* End of File */
  28306.  
  28307.  
  28308. Listing 9 Example image operator
  28309. /*
  28310. * Listing 9 - flipy.c
  28311. */
  28312.  
  28313. #include <stdio.h>
  28314. #include "ips_image.h"
  28315.  
  28316. void flipy();
  28317.  
  28318. int
  28319. main(argc, argv)
  28320. int argc;
  28321. char **argv;
  28322. {
  28323. ips_header header;
  28324. ips_image image, newimage;
  28325.  
  28326. if (argc<3) {
  28327. printf("Usage: %s infile outfile", argv[0]);
  28328. exit(1);
  28329. }
  28330.  
  28331. ips_to_img(&header, &image, argv[1]);
  28332.  
  28333. flipy(&header, &image, &newimage);
  28334.  
  28335. img_to_ips(&header, &newimage, argv[2]);
  28336.  
  28337. exit(0);
  28338. }
  28339.  
  28340.  
  28341. void
  28342. flipy(header, image, newimage)
  28343. ips_header *header;
  28344. ips_image *image, *newimage;
  28345. {
  28346. long cnt;
  28347.  
  28348.  
  28349. memcpy(newimage->cmap, image->cmap, sizeof(XColor) * 256);
  28350.  
  28351. newimage->data =
  28352. (unsigned char *)malloc(header->width *
  28353. header->height);
  28354.  
  28355. for(cnt=0;cnt < header->width * header->height;cnt++)
  28356. newimage->data[cnt] =
  28357. image->data[(header->width * header->height) -
  28358. cnt];
  28359. }
  28360. /* End of File */
  28361.  
  28362.  
  28363.  
  28364.  
  28365.  
  28366.  
  28367.  
  28368.  
  28369.  
  28370.  
  28371.  
  28372.  
  28373.  
  28374.  
  28375.  
  28376.  
  28377.  
  28378.  
  28379.  
  28380.  
  28381.  
  28382.  
  28383.  
  28384.  
  28385.  
  28386.  
  28387.  
  28388.  
  28389.  
  28390.  
  28391.  
  28392.  
  28393.  
  28394.  
  28395.  
  28396.  
  28397.  
  28398.  
  28399.  
  28400.  
  28401.  
  28402.  
  28403.  
  28404.  
  28405.  
  28406.  
  28407.  
  28408.  
  28409.  
  28410.  
  28411. Literate Programming in C and C++ using CWEB
  28412.  
  28413.  
  28414. Lee Wittenberg
  28415.  
  28416.  
  28417. Lee Wittenberg teaches Computer Science at Kean College of New Jersey. He is
  28418. an active contributor to the Literate Programming electronic discussion group,
  28419. and can be reached via the Internet at leew@pilot.njin.net.
  28420.  
  28421.  
  28422.  
  28423.  
  28424. Introduction
  28425.  
  28426.  
  28427. A quiet revolution is taking place in programming circles -- a revolution
  28428. called literate programming. In 1972, Edsger Dijkstra [1] wished for a
  28429. "program written down as I can understand it, I want it written down as I
  28430. would like to explain it to someone." Ten years later, Donald Knuth developed
  28431. the original WEB system, coining the phrase "literate programming" in the
  28432. process [2].
  28433. Literate programming is precisely what Dijkstra wanted: the ability to write a
  28434. program as you would explain it to another human being, rather than as your
  28435. compiler would have it. In the decade since Knuth introduced the idea, the
  28436. number of literate programming tools has proliferated. In addition to the
  28437. original WEB, FunnelWeb, FWEB, noweb, Nuweb, Spidery WEB, and WinWordWEB are
  28438. all available and in active use. But the flagship of the WEB fleet is CWEB, a
  28439. preprocessor that supports literate programming in both C and C++, and (with
  28440. the recent publication of "The Stanford GraphBase" [4] and "The CWEB System of
  28441. Structured Documentation" [5]) is slowly moving into the mainstream, both in
  28442. industry and academia.
  28443.  
  28444.  
  28445. CWEB Program Format
  28446.  
  28447.  
  28448. A CWEB program, commonly called a web, is made up of sections, each of which
  28449. contains a text part and a code part, or chunk. The chunks can be extracted to
  28450. create a program for compilers, or the entire web can be typeset to be read by
  28451. people. A single source file serves the divergent interests of human and
  28452. machine.
  28453. Listing 1 is an example of a simple web. It shows how the traditional "hello
  28454. world" program might look if written in CWEB. The sections are numbered, the
  28455. code parts are cross-referenced by section number, and the identifiers and
  28456. chunk names are indexed. Following Algol conventions, keywords are typeset in
  28457. boldface, identifiers in italics, and strings in a typewriter font for easier
  28458. reading.
  28459. More important to note is the program layout. Instead of putting the line
  28460. #include <stdio.h> at the beginning of the program, I use < Header files
  28461. needed by the program 3 > as a placeholder. At this stage of the game, I know
  28462. I'll be using header files, but -- in the best tradition of structured
  28463. programming -- I'm not yet ready to make a decision about which ones I will
  28464. need, so I leave the details up in the air until I know more. I can do the
  28465. same thing with the body of main. In other words, I organize the program for
  28466. the human reader, not the compiler.
  28467. Each section of the web is a self-contained, self-explanatory piece of the
  28468. program. Listing 1 amounts to overkill for such a tiny, well-known program,
  28469. but Literate Programming is invaluable in dealing with larger programs, as
  28470. Listing 4 will show.
  28471. Listing 2 is the source file as I actually typed it, which CWEB used to
  28472. generate Listing 1. This source file is basically a C program with embedded
  28473. "markup" strings to delineate the literate programming features. Sections are
  28474. introduced by @* or @ plus space followed by the text part. The code part of a
  28475. section begins with @c, or with a chunk name followed by = . Chunk names are
  28476. enclosed in @< and @>. Vertical bars surround bits of code embedded in text.
  28477.  
  28478.  
  28479. Weaving and Tangling
  28480.  
  28481.  
  28482. Generating a human-readable document from a marked-up source file is called
  28483. "weaving the web." The CWEAVE command generates the section numbers,
  28484. cross-references, and index. It also typesets the text and code properly, with
  28485. occasional hints from the programmer (the control codes @; and @# are examples
  28486. of such hints).
  28487. The analogous procedure for generating a compiler-readable file is called
  28488. tangling. Listing 3 shows the result of running the web in Listing 2 through
  28489. CTANGLE. CTANGLE expands the @c chunk, recursively replacing chunk names with
  28490. their definitions. If you look carefully, you can spot the classic program
  28491. peeking through. CTANGLE inserts #line directives in the output file so the
  28492. compiler and debugger can refer to the original web rather than the generated
  28493. C file. Humans rarely need to look at tangled output, but for the sake of
  28494. those who do, CTANGLE generates comments that link the tangled code to the
  28495. numbered sections in the woven document.
  28496.  
  28497.  
  28498. Creating a CWEB Program
  28499.  
  28500.  
  28501. Since literate programs are meant to be read, I'm going to ask you to read
  28502. through the program fragment in Listing 4. Before you do, however, I should
  28503. warn you that CWEB provides a few notational conveniences that traditional C
  28504. programmers often find disconcerting at first. CWEB replaces the symbols
  28505. listed in Table 1 by mathematical equivalents. This substitution serves a dual
  28506. purpose; it improves the readability of some of the more obscure operators,
  28507. and provides a somewhat language-independent "publication language" for C.
  28508. (WEB uses the same symbols for typesetting Pascal, and FWEB and Spidery WEB do
  28509. likewise for the languages they support.) You can customize CWEB so it will
  28510. typeset these symbols any way you like when you weave a web. However, you may
  28511. find, as I do, that you prefer the "standard" CWEB symbols.
  28512. Listing 4 demonstrates many of the advantages of literate programming. You can
  28513. read the web straight through, or use the cross-referencing to follow a single
  28514. thread. In addition to identifiers, the index contains references to
  28515. "portability problems," "possible improvements," and other potentially useful
  28516. items. "System dependencies" is a common index entry in webs. Reference lists,
  28517. acknowledgements, and other useful bits of information not normally found in
  28518. program documentation are common in literate programs.
  28519. I wrote the program pretty much straight through, starting with section 1
  28520. through section 20. [Sections 10 through 20 have been omitted here to save
  28521. space. The complete listings are available on this month's code disk, as well
  28522. as the original webs (.w files) and PostScript versions of the woven webs --
  28523. mb.] I added sections 21-24 as they became necessary, and CWEAVE generated
  28524. section 25 automatically. I proceeded topdown, starting with the main program
  28525. and using chunk names to implement a process of stepwise refinement, keeping
  28526. each section small and easy to understand. Since chunk names are
  28527. typographically distinct from the actual code, they are much easier to read
  28528. than function or macro names.
  28529. For each code chunk, I thought about what it should do and how it should work,
  28530. writing down these thoughts as the text part of a section. I then wrote the
  28531. code part to meet these specifications. When you write a literate program, you
  28532. usually write the code to match the documentation, rather than the other way
  28533. around.
  28534. I also like to include explanations of why I did (or didn't) do things in a
  28535. particular way. Observations that would be intrusive in conventional program
  28536. comments fit quite well in a literate program. I can use all of the techniques
  28537. of word processing -- table of contents, footnotes, charts, graphs, etc. -- to
  28538. better explain my code.
  28539. Some of the benefits of literate programming as applied to Listing 4 do not
  28540. show up in the final result. For example, I worked on this program over
  28541. several months, leaving it alone for weeks at a time, when more pressing
  28542. business called. I only had to read through the program once in order to pick
  28543. up where I left off. It would have taken a full day, at least, with a
  28544. non-literate program.
  28545.  
  28546.  
  28547. C++ and CWEB
  28548.  
  28549.  
  28550. Listing 5 is an example of a web that implements a C++ class. Since C++ syntax
  28551. is more complex than that for C, you generally have to give CWEB more hints to
  28552. get the prettyprinting correct, but the process is the same.
  28553. CWEB allows a programmer to develop a class interface and implementation in
  28554. lock-step. For example, I define the Xstring constructor and destructor
  28555. interfaces in section 5, and follow immediately with their implementation in
  28556. sections 6-11 [sections 8 through 17 not shown -- mb]. CTANGLE automatically
  28557. extracts the class declaration in the <xstring.h> chunk to create the
  28558. xstring.h file (because the chunk is named with @( instead of @<), so I don't
  28559. have to worry about keeping information consistent across two separate files.
  28560.  
  28561.  
  28562. Producing Formatted Output
  28563.  
  28564.  
  28565. The CWEAVE program does not produce its formatted output directly. It produces
  28566. a file that is then processed by the TeX (pronounced "Tekh") typesetting
  28567. system. TeX is the typesetting system of choice for literate programming
  28568. systems, for a variety of reasons:
  28569.  
  28570. 1. TeX is readily available. Implementations exist for pretty much every
  28571. computer in existence, and are usually available free of charge. I use the
  28572. excellent emTeX implementation for MS-DOS, by Eberhard Mattes, which is freely
  28573. distributable.
  28574. 2. TeX is portable. It is designed to be as machine-independent as possible.
  28575. Output generated by emTeX on my PC and that generated by, say, a UNIX
  28576. implementation will be identical.
  28577. 3. TeX is stable. Before a program can be certified as TeX, it must pass a
  28578. rigorous test suite called the "trip test." Since TeX is not a commercial
  28579. product, it is not subject to the whims of a manufacturer.
  28580. 4. The quality of TeX output is significantly better than that of any
  28581. commercial word processor or desktop publisher on the market today.
  28582. On the other hand, there is no reason a literate programming system has to use
  28583. TeX. A number of non-TeX systems exist, and many are suitable for programming
  28584. in C and C++.
  28585.  
  28586.  
  28587. Availability
  28588.  
  28589.  
  28590. Although it is not in the public domain, CWEB is freely distributable. The
  28591. official distribution is via anonymous ftp from labrea.stanford.edu on the
  28592. Internet. The distribution includes source for UNIX, MS-DOS, VMS, and Amiga
  28593. distribution. This version is also available on this month's code disk and via
  28594. the online sources listed on page 3.
  28595. TeX is available via ftp from the following sites:
  28596.  
  28597.  
  28598. Host
  28599.  
  28600.  
  28601. ftp.tex.ac.uk
  28602. ftp.dante.de
  28603. ftp.shsu.edu
  28604. After logging in, cd to directory tex-archive/systems, then cd to appropriate
  28605. subdirectory (msdos, unix, mac, etc.) for machine-specific TeX materials.
  28606. Complete TeX systems for Amiga, Atari, Macintosh, MS-DOS, OS/2, UNIX, VM/CMS,
  28607. VMS, and Windows NT are available on the Prime Time TeXcetera 1-1 collection
  28608. on CD-ROM, available from R&D Publications for $60.00 plus $3.50 shipping and
  28609. handling. Contact:
  28610. R&D Publications
  28611. 1601 W. 23rd St.
  28612. Lawrence,KS 66046
  28613. (913)-841-1631
  28614. michelle@rdpub.com
  28615. If you would like to find out more about literate programming, I recommend you
  28616. subscribe to the LitProg discussion group on the Internet. All discussion is
  28617. via electronic mail, so it should be possible to subscribe from CompuServe, or
  28618. any other service that can send and receive Internet mail. To subscribe, send
  28619. a message to LISTSERV@shsu.edu, and include
  28620. SUBSCRIBE LitProg "your name"
  28621. in the body of the message. If you prefer, you can subscribe to the
  28622. comp.programming.literate newsgroup. All items posted to the newsgroup are
  28623. automatically distributed to LitProg subscribers, and vice versa.
  28624.  
  28625.  
  28626. Conclusion
  28627.  
  28628.  
  28629. Literate programming is not for the beginner. The literate programmer must be
  28630. proficient in at least two languages: a programming language, like C or C++,
  28631. and a text formatting language, like TeX or a commercial word processor. Since
  28632. the bulk of a web is explanation, rather than code, the programmer has to take
  28633. the time to think through the program in order to be able to explain it. As in
  28634. any programming methodology, time spent in thought "up front" translates into
  28635. reduced debugging and easier maintenance. What literate programming adds to
  28636. the mix is that the programmer's thoughts no longer disappear into thin air
  28637. once the program is written; they are preserved in a web. The programmer who
  28638. maintains the web has these thoughts as a foundation for future work. For the
  28639. professional programmer, maintenance is everything. A literate program is a
  28640. maintainable program.
  28641. But, above all, literate programming is fun. As you write your explanations,
  28642. the code unfolds naturally, as if it had always been there and you just now
  28643. happened to find it. The phenomenon of "code that seems to write itself" is
  28644. common among literate programmers, as is the practice of passing programs
  28645. around for comments. The tedium of rearranging bits of code to cater to a
  28646. compiler is gone. In its place is a process of discovery.
  28647.  
  28648.  
  28649. Acknowledgements
  28650.  
  28651.  
  28652. I'd like to thank Danielle Bernstein, Leonard Ginsburg, and Jack Ryder, whose
  28653. comments and criticism greatly improved this article.
  28654. References
  28655. [1] Edsger W. Dijkstra. Structured Programming (Academic Press, 1972), "Notes
  28656. on structured programming," pp. 1-82.
  28657. [2] Donald E. Knuth. "Literate programming," The Computer Journal, May: 1984,
  28658. pp. 97-111. Reprinted in Knuth's Literate Programming (Stanford University
  28659. Center for the Study of Language and Information, 1992), pp. 99-135.
  28660. [3] Donald E. Knuth. Computers and Typesetting (Addison-Wesley, 1986), Volume
  28661. A--"The TeXbook."
  28662. [4] Donald E. Knuth. The Stanford Graph-Base: A Platform for Combinatorial
  28663. Computing. A collection of CWEB programs for generating and examining a wide
  28664. variety of graphs and networks. (ACM Press, 1994).
  28665. [5] Donald E. Knuth and Silvio Levy. The CWEB System of Structured
  28666. Documentation (Addison-Wesley, 1994). ISBN 0-201-57569-8.
  28667.  
  28668.  
  28669. Additional Information
  28670.  
  28671.  
  28672. Walsh, Norman. Making Tex Work. O'Reilly & Associates, 1994. ISBN
  28673. 1-56592-051-1.
  28674. LitProg FAQ. FTP from the TeX archive sites listed in main article, directory
  28675. help/LitProg- FAQ.
  28676. Table 1 Symbols typeset by CWEB
  28677. Standard C CWEB
  28678. -----------------
  28679.  = Â¬
  28680.  
  28681.  == Âº
  28682.  != 
  28683.  <= Â£
  28684.  >= >=
  28685.  && 
  28686.  
  28687.  ! Â¬
  28688.  ^ Ã…
  28689.  NULL L
  28690.  
  28691. Listing 1 A web for "hello, world"
  28692. -------------------------------------------------------------
  28693. A Simple Example ........................................ 1
  28694. Index ................................................... 4
  28695. -------------------------------------------------------------
  28696. 1. A Simple Example. This is a trivial example of a CWEB
  28697. program. It is, of course, the classic "hello, world" program we all
  28698. know and love:
  28699. á Header files needed by the program 3 Ã±
  28700. main (void)
  28701. {
  28702. á Print the message "hello, world" 2 Ã±
  28703. }
  28704.  
  28705. 2. Naturally, we use printf to do the dirty work:
  28706. á Print the message "hello, world" 2 Ã± Âº
  28707. printf ("hello, world\n");
  28708. This code is used in section 1.
  28709.  
  28710. 3. The prototype for printf is in the standard header, <stdio.h>.
  28711. á Header files needed by the program 3 Ã± Âº
  28712. #include <stdio.h>
  28713. This code is used in section 1.
  28714.  
  28715. -------------------------------------------------------------
  28716. 4. Index.
  28717. main: 1. printf: 2, 3.
  28718. -------------------------------------------------------------
  28719. á Header files needed by the program 3 Ã± Used in section 1.
  28720. á Print the message "hello, world" 2 Ã± Used in section 1.
  28721.  
  28722.  
  28723. Listing 2 The text file that produced Listing 1
  28724. \def\title{Listing 1}
  28725.  
  28726. @*A Simple Example.
  28727. This is a trivial example of a \.{CWEB} program.
  28728. It is, of course, the classic "hello, world"
  28729. program we all know and love:
  28730.  
  28731. @c
  28732. @<Header files needed by the program@>@;
  28733. @#
  28734. main(void)
  28735. {
  28736. @<Print the message "hello, world"@>@;
  28737. }
  28738.  
  28739. @ Naturally, we use printf to do the dirty work:
  28740.  
  28741.  
  28742. @<Print the message "hello, world"@>=
  28743. printf("hello, world\n");
  28744.  
  28745. @ The prototype for printf is in the standard
  28746. header, \.{<stdio.h>}.
  28747.  
  28748. @<Header files needed by the program@>=
  28749. #include <stdio.h>
  28750.  
  28751. @*Index.
  28752.  
  28753.  
  28754. Listing 3 Compiler-readable result of "tangling" Listing 2
  28755. /*1:*/
  28756. #line 8 "hello.w"
  28757.  
  28758. /*3:*/
  28759. #line 24 "hello.w"
  28760.  
  28761. #include <stdio.h>
  28762.  
  28763. /*:3*/
  28764. #line 9 "hello.w"
  28765.  
  28766. main(void)
  28767. {
  28768. /*2:*/
  28769. #line 18 "hello.w"
  28770.  
  28771. printf("hello, world\n");
  28772.  
  28773. 1*:2*/
  28774. #line 13 "hello.w"
  28775.  
  28776. }
  28777.  
  28778. /*:1*/
  28779. /* End of File */
  28780.  
  28781.  
  28782. Listing 4 A more elaborate example of Literate Programming
  28783. -------------------------------------------------------------
  28784. The Problem.............................................. 1
  28785. The Solution............................................. 2
  28786. Processing input lines............................... 6
  28787. Command line options................................ 17
  28788. Error messages...................................... 21
  28789. References.............................................. 24
  28790. Index................................................... 25
  28791. -------------------------------------------------------------
  28792. Copyright (C) 1994 by Lee Wittenberg.
  28793. -------------------------------------------------------------
  28794. 1. The Problem. Rather than make up a problem, we'll
  28795. attempt to solve an exercise from The C Programming Language
  28796. [1] using CWEB rather than plain C. In particular, we've chosen
  28797. the following problem from page 34 of the second edition:*
  28798.  
  28799. Exercise 1-22. Write a program to "fold" long
  28800.  
  28801. input lines into two or more shorter lines after the
  28802. last non-blank character that occurs before the
  28803. n-th column of input. Make sure your program
  28804. does something intelligent with very long lines,
  28805. and if there are no blank or tabs before the
  28806. specified column.
  28807.  
  28808. This seems to imply that n = 80 requires output lines to contain
  28809. at most 79 columns. It seems a bit more logical to let the user
  28810. specify the maximum number of columns in the output, so that
  28811. n = 80 will give us output lines of Â£ 80 characters. Since the
  28812. problems are isomorphic (a solution to either is an "off by
  28813. one" error for the other), we choose to solve the latter.
  28814. -------------------------------------------------------------
  28815. * A similar problem appears on page 31 of the first edition.
  28816. -------------------------------------------------------------
  28817. 2. The Solution. The structure of our program is fairly
  28818. standard, and doesn't need a lot of explanation. After processing
  28819. any command line options, we copy input lines to the standard
  28820. output, folding them when necessary.
  28821. Since we process all the options before we process any
  28822. files, different options cannot be used for separate files.
  28823. á Header files 3 Ã±
  28824. á Global variables 5 Ã±
  28825. á Functions 7 Ã±;
  28826. main (int argc, char *argv[])
  28827. {
  28828. á Scan command line options 19 Ã±;
  28829. á Allocate space for the input buffer 4 Ã±;
  28830. á Copy the input to the standard output,
  28831. folding lines as necessary 6 Ã±;
  28832. return EXIT_SUCCESS;
  28833. }
  28834.  
  28835. 3. Ã¡ Header files 3 Ã±Âº
  28836. #include <stdlib.h>
  28837. /* for EXIT_SUCCESS */
  28838. See also sections 8 and 14.
  28839. This code is used in section 2.
  28840.  
  28841. 4. Since we don't want to place any unnecessary restrictions on
  28842. line length, or on allowable values of n, we allocate space for
  28843. the input buffer dynamically. We grab one character more
  28844. than we need for a line, just in case a line contains exactly
  28845. fold_column characters or we have a space in exactly the perfect
  28846. spot (plus a byte for the '\0', of course).
  28847. Since this is the only memory allocation we do, the malloc
  28848. shouldn't fail, but you never can tell.
  28849. á Allocate space for the input buffer 4 Ã± Âº
  28850. buffer Â¬ (char *) malloc (fold_column + 2);
  28851. if (buffer Âº L) {
  28852. á Announce that we ran out of heap space 22 Ã±;
  28853. exit(EXIT_FAILURE);
  28854. }
  28855. This code is used in section 2.
  28856.  
  28857. 5. Unless the user specifies otherwise, we assume that folding
  28858. will occur after the 80th column.
  28859. #define DEFAULT_FOLD 80U
  28860.  
  28861. á Global variables 5 Ã± Âº
  28862. char *buffer;
  28863. size_t fold_column Â¬ DEFAULT_FOLD;
  28864. See also section 18.
  28865. This code is used in section 2.
  28866.  
  28867. 6. Processing input lines. We assume that once we're ready to
  28868. deal with input lines, the contents of argv have been
  28869. "normalized" -- that all arguments that do not represent filenames have been
  28870. replaced with null pointers. This will make our job a bit easier.
  28871. We also assume that the string "-" used as a filename refers
  28872. to the standard input.
  28873. á Copy the input to the standard output, folding lines as
  28874. necessary 6 Ã± Âº
  28875. if (á No file names were specified 17 Ã±)
  28876. fold_file ("-")
  28877. else {
  28878. int i;
  28879. for (i Â¬ 1; i < argc; i++) {
  28880. if (argv[i] L)
  28881. fold_file (argv[i]);
  28882. }
  28883. }
  28884. This code is used in section 2.
  28885.  
  28886. 7. The actual line-folding is done by the function fold_file,
  28887. which takes the name of a file to be folded as its only argument.
  28888. The filename "-" is taken to mean "use the standard input."
  28889. á Functions 7 Ã± Âº
  28890. void fold_file (const char *filename)
  28891. {
  28892. FILE *infile;
  28893. á Local variables for fold_file 10 Ã±;
  28894. if (strcmp (filename, "-" Âº 0)
  28895. infile Â¬ stdin;
  28896. else {
  28897. infile Â¬ fopen (filename, "r" );
  28898. if (infile Âº L) {
  28899. á Warn the user that we couldn't open
  28900. filename 21 Ã±;
  28901. return;
  28902. }
  28903. }
  28904. á Copy infile to stdout, folding lines as necessary 9 Ã±;
  28905. if (infile stdin)
  28906. fclose (infile);
  28907. }
  28908. This code is used in section 2.
  28909.  
  28910. 8. < Header files 3 > +º
  28911. #include <stdio.h>
  28912. #include <string.h>
  28913.  
  28914. 9. Whenever we fold an input line, we leave the portion after the
  28915. fold in buffer. We use left_overs to let us know how much of
  28916. buffer has already been used, and consequently, how much space
  28917. is available for reading the rest of the line. The extra 2 characters
  28918. specified in "fold_column - left_overs + 2" are for the '\n' and '\0'.
  28919. á Copy infile to stdout, folding lines as necessary 9 Ã± Âº
  28920. while (fgets (buffer + left_overs,
  28921. fold_column - left_overs + 2, infile)L)
  28922.  
  28923. {
  28924. á Fold input line in buffer, if necessary 11 Ã±;
  28925. }
  28926. if (left_overs 0) /* incomplete last line */
  28927. fprintf (stdout, "%.*s", (int) left_overs, buffer);
  28928. This code is used in section 7.
  28929. -------------------------------------------------------------
  28930. [sections 10 through 20 omitted -- mb]
  28931. -------------------------------------------------------------
  28932. 21. Error messages. In a literate program, it is often helpful to
  28933. the reader if all of the error handling code is described in the
  28934. same place. This also helps the programmer make sure that the
  28935. style of the messages is consistent throughout the program.
  28936. á Warn the user that we couldn't open filename 21 Ã± Âº
  28937. fprintf(stderr,
  28938. "I couldn't open the file \"%s\"\n.",filename);
  28939. This code is used in section 7.
  28940.  
  28941. 22. Ã¡ Announce that we ran out of heap space 22 Ã± Âº
  28942. fprintf (stderr,
  28943. "I couldn't allocate needed memory. Sorry.\n");
  28944. This code is used in section 4.
  28945.  
  28946. 23. Ã¡ Tell user about unknown option in argv[i] 23 Ã± Âº
  28947. fprintf(stderr, "I don't know the '%s'
  28948. option; I'll ignore it. \n", argv[i]);
  28949.  
  28950. This code is used in section 20.
  28951.  
  28952. 24. References.
  28953. [1] Brian W. Kernighan and Dennis M. Ritchie. The C
  28954. Programming Language. Prentice-Hall, second edition, 1988.
  28955.  
  28956. 25. Index
  28957. "-" as a filename: 6,7, 19. infile: 7, 9.
  28958. argc: 2, 6, 17, 19. is_option: 19.
  28959. argv: 2, 6, 18, 19, 20, 23. isdigit: 20.
  28960. argv normalization: 6, 19. isspace: 13, 15, 16.
  28961. buffer: 4, 5, 9, 11, 13, 15, 16. left_overs: 9, 10, 11, 16.
  28962. DEFAULT_FOLD: 5. main: 2.
  28963. exit: 4. malloc: 4.
  28964. EXIT_FAILURE: 4. opt_count: 17, 18, 19.
  28965. EXIT_SUCCESS: 2, 3. paranoid error checks: 4.
  28966. fclose: 7. portability problems: 15.
  28967. fgets: 9. possible improvements: 16, 20.
  28968. filename: 7, 21. ptr: 12, 13, 15, 16.
  28969. fold_column: 4, 5, 9, 11, 13, stderr: 21, 22, 23.
  28970. 15, 16, 20. stdin: 7.
  28971. fold_file: 6, 7. stdout: 9, 11, 16.
  28972. fopen: 7. strcmp: 7.
  28973. fprintf: 9, 16, 21, 22, 23. strcpy: 16.
  28974. fputs: 11 strlen: 11, 16.
  28975. i: 6, 19. strtoul: 20.
  28976. incomplete specifications: 15, 16.
  28977. -------------------------------------------------------------
  28978. á Allocate space for the input buffer 4 Ã± Used in section 2.
  28979. á Announce that we ran out of heap space 22 Ã± Used in section 4.
  28980. á Copy the input to the standard output, folding lines
  28981. as necessary 6 Ã± Used in section 2.
  28982.  
  28983. á Copy infile to stdout, folding lines as necessary 9 Ã±
  28984. Used in section 7.
  28985. á Deal with specification flaw 15 Ã± Used in section 13.
  28986. á Determine an appropriate folding point, and fold the line 12 Ã±
  28987. Used in section 11.
  28988. á Fold input line in buffer, if necessary 11 Ã± Used in section 9.
  28989. á Fold buffer at the place specified by ptr 16 Ã± Used in section 12.
  28990. á Functions 7 Ã± Used in section 2.
  28991. á Global variables 5, 18 Ã± Used in section 2.
  28992. á Header files 3, 8, 14 Ã± Used in section 2.
  28993. á Local variables for fold_file 10 Ã± Used in section 7.
  28994. á No file names were specified 17 Ã± Used in section 6.
  28995. á Point ptr at the appropriate place in buffer for the fold 13 Ã±
  28996. Used in section 12.
  28997. á Process the option in argv[i] 20 Ã± Used in section 19.
  28998. á Scan command line options 19 Ã± Used in section 2.
  28999. á Tell user about unknown option in argv[i] 23 Ã± Used in section 20.
  29000. á Warn the user that we couldn't open filename 21 Ã± Used in section 7.
  29001.  
  29002.  
  29003. Listing 5 Using CWeb with C++
  29004. -------------------------------------------------------------
  29005. A C++ String Class...................................... 1
  29006. Representing an Xstring................................ 4
  29007. Construction and Destruction........................... 5
  29008. Assignment............................................. 12
  29009. Miscellaneous Operations............................... 14
  29010. References.............................................. 18
  29011. Index................................................... 19
  29012. -------------------------------------------------------------
  29013. Copyright (C) 1994 by Lee Wittenberg.
  29014. Portions copyright (C) 1991 by AT&T Bell Telephone
  29015. Laboratories, Inc.
  29016. -------------------------------------------------------------
  29017. 1. A C++ String Class. To demonstrate the use of CWEB for C++
  29018. programming, we adapt the string class described by Stroustrup
  29019. [1, pages 248-251]. Explanations in slanted type (including
  29020. inline comments, when possible) are direct quotes from the
  29021. original. We make a few minor changes along the way, but on the
  29022. whole, we stick to Stroustrup's design.
  29023.  
  29024. 2. We put the interface part of our class in the header file
  29025. xstring.h. We call our class "Xstring" rather than "string" to
  29026. avoid confusion with the original and other (more useful) string
  29027. classes. We restrict ourselves to a lowercase file name to
  29028. maintain portability among operating systems with case-insensitive
  29029. file names.
  29030. á xstring.h 2 Ã± Âº
  29031. #ifndef XSTRING_H
  29032. #define XSTRING_H
  29033. //prevent multiple inclusions
  29034. class Xstring {
  29035. á Private Xstring members 4 Ã±
  29036. public:
  29037. á Public Xstring members 5 Ã±
  29038. };
  29039. #endif
  29040. This code is cited in section 3.
  29041. This code is used in section 3.
  29042.  
  29043.  
  29044. 3. We implement the class members in a single "unnamed
  29045. chunk" that will be tangled to xstring.c (or xstring.cc or
  29046. xstring.cpp, depending on your compiler's preference). We
  29047. include the contents of < xstring. h 2 ) directly, rather than relying
  29048. on #include, because we can.
  29049. á Header files 8 Ã±
  29050. á xstring.h 2ñ
  29051. á Xstring members and friends 6 Ã±
  29052.  
  29053. -------------------------------------------------------------
  29054. 4. Representing an Xstring. The internal representation of an
  29055. Xstring is simple. It counts the references to a string to
  29056. minimize copying and uses standard C++ character strings as
  29057. constants.
  29058. á Private Xstring members 4 Ã± Âº
  29059. struct srep {
  29060. char *s; // pointer to data
  29061. int n;// reference count
  29062. srep() { n Â¬ 1; }
  29063. };
  29064. srep *p;
  29065. See also section 16.
  29066. This code is used in section 2.
  29067.  
  29068. -------------------------------------------------------------
  29069. 5. Construction and Destruction. The constructors and the
  29070. destructor are trivial. We use the null string as a default
  29071. constructor argument rather than a null pointer to protect against possible
  29072. string. h function anomalies.
  29073. á Public Xstring members 5 Ã± Âº
  29074. Xstring(const char *s Â¬ "");
  29075. // Xstring x Â¬ "abc"
  29076. Xstring(const Xstring &);
  29077. // Xstring x Â¬ Xstring ...
  29078. ~Xstring();
  29079. See also sections 12, 14, and 15.
  29080. This code is used in section 2.
  29081.  
  29082. 6. An Xstring constructed from a standard string needs space to
  29083. hold the characters:
  29084. á Xstring members and friends 6 Ã± Âº
  29085. Xstring::Xstring(const char *s)
  29086. {
  29087. p Â¬ new srep;
  29088. á Allocate space for the string and put a copy of s there 7 Ã±;
  29089. }
  29090. See also sections 9, 10, 13, and 17.
  29091. This code is used in section 3.
  29092.  
  29093. 7. There is always the possibility that a client will try something
  29094. like "Xstring x Â¬ L." We substitute the null string whenever we are given a
  29095. null pointer.
  29096. á Allocate space for the string and put a copy of s there 7 Ã± Âº
  29097. if (s Âº L) s Â¬ "";
  29098. p(R) s Â¬ new char [strlen (s) + 1 ];
  29099. strcpy (p(R) s, s);
  29100. This code is used in sections 6 and 13.
  29101. -------------------------------------------------------------
  29102. [Sections 8 through 17 omitted -- mb]
  29103. -------------------------------------------------------------
  29104.  
  29105. 18. References.
  29106. [1] Bjarne Stroustrup. The C++ Programming Language.
  29107. Addison-Wesley, second edition, 1991.
  29108.  
  29109. -------------------------------------------------------------
  29110. 19. Index.
  29111. dummy: 15, 16, 17. strcat: 14.
  29112. i: 15. strcpy: 7, 8.
  29113. n: 4. strlen: 7, 14, 15.
  29114. operator: 12, 13, 14, 15. x: 5, 7, 9, 13.
  29115. p: 4. Xstring: 2, 6, 9, 10, 13.
  29116. s: 4, 5, 6, 13. XSTRING_H: 2.
  29117. srep: _4, 6, 13.
  29118. -------------------------------------------------------------
  29119. á Allocate space for the string and put a copy of s there 7 Ã±
  29120. Used in sections 6 and 13.
  29121. á Decrement reference count, and remove p if necessary 11 Ã±
  29122. Used in sections 10 and 13.
  29123. á Header files 8 Ã± Used in section 3.
  29124. á Private Xstring members 4, 16 Ã± Used in section 2.
  29125. á Public Xstring members 5, 12, 14, 15 Ã± Used in section 2.
  29126. á xstring.h 2 Ã± Cited in section 3. Used in section 3.
  29127. á Xstring members and friends 6, 9, 10, 13, 17 Ã± Used in section 3.
  29128.  
  29129.  
  29130.  
  29131.  
  29132.  
  29133.  
  29134.  
  29135.  
  29136.  
  29137.  
  29138.  
  29139.  
  29140.  
  29141.  
  29142.  
  29143.  
  29144.  
  29145.  
  29146.  
  29147.  
  29148.  
  29149.  
  29150.  
  29151.  
  29152.  
  29153.  
  29154.  
  29155.  
  29156.  
  29157.  
  29158.  
  29159.  
  29160.  
  29161.  
  29162.  
  29163.  
  29164.  
  29165.  
  29166.  
  29167.  
  29168. Understanding the C Standard
  29169.  
  29170.  
  29171. Clive D.W. Feather
  29172.  
  29173.  
  29174. This article is not available in electronic form.
  29175.  
  29176.  
  29177.  
  29178.  
  29179.  
  29180.  
  29181.  
  29182.  
  29183.  
  29184.  
  29185.  
  29186.  
  29187.  
  29188.  
  29189.  
  29190.  
  29191.  
  29192.  
  29193.  
  29194.  
  29195.  
  29196.  
  29197.  
  29198.  
  29199.  
  29200.  
  29201.  
  29202.  
  29203.  
  29204.  
  29205.  
  29206.  
  29207.  
  29208.  
  29209.  
  29210.  
  29211.  
  29212.  
  29213.  
  29214.  
  29215.  
  29216.  
  29217.  
  29218.  
  29219.  
  29220.  
  29221.  
  29222.  
  29223.  
  29224.  
  29225.  
  29226.  
  29227.  
  29228.  
  29229. Standard C/C++
  29230.  
  29231.  
  29232. Implementing <fstream>
  29233.  
  29234.  
  29235.  
  29236.  
  29237. P.J. Plauger
  29238.  
  29239.  
  29240. P.J. Plauger is senior editor of C/C++ Users Journal. He is convener of the
  29241. ISO C standards committee, WG14, and active on the C++ committee, WG21. His
  29242. latest books are The Draft Standard C++ Library, and Programming on Purpose
  29243. (three volumes), all published by Prentice-Hall. You can reach him at
  29244. pjp@plauger.com.
  29245.  
  29246.  
  29247.  
  29248.  
  29249. Introduction
  29250.  
  29251.  
  29252. Last month, I introduced the header <fstream> from the draft Standard C++
  29253. library. (See "Standard C: The Header <fstream>," CUJ, April 1995.) It
  29254. provides the classes you need to read and write external files and devices, in
  29255. the guise of extracting from an istream object or inserting to an ostream
  29256. object. I continue this month with a description of one way to implement these
  29257. classes.
  29258. This implementation should be largely familiar to those of you with some
  29259. experience with existing iostreams packages. While the draft C++ Standard has
  29260. introduced various small changes from traditional technology, char-based file
  29261. I/O is heavily rooted in existing practice. The really new stuff is all the
  29262. templates introduced into the library. I plan to avoid talking about them for
  29263. a bit longer, at least.
  29264. Listing 1 shows the file fstream, which implements the standard header
  29265. <fstream>. It defines the classes filebuf, ifstream, ofstream, stdiobuf,
  29266. istdiostream, and ostdiostream. I begin with several notes on class filebuf,
  29267. which is the workhorse class for this header.
  29268. The incomplete type declaration struct _Filet is an alias for the type FILE,
  29269. declared in <stdio.h>. The header <fstream> is not permitted to include
  29270. <stdio.h>, but it needs to declare parameters and member function return
  29271. values compatible with type FILE. A secret synonym solves the problem. My
  29272. implementation of the Standard C library [1] introduces _Filet for a similar
  29273. reason. For another implementation, you may have to alter the name, or
  29274. introduce extra machinery, to achieve the same effect.
  29275. (A recent addition to C++ is the ability to gather multiple declarations into
  29276. separate namespaces. The entire Standard C++ library then inhabits the
  29277. namespace std. With such protection, an implementation can more safely include
  29278. C headers in C++ headers -- though a few problems with macro names have yet to
  29279. be widely discussed. Until namespace support becomes widespread, however, the
  29280. sort of implementation presented here is prudent.)
  29281. I added a constructor with the signature filebuf(_Filet *). (I did so by
  29282. adding a default argument to the default constructor.) This implementation
  29283. performs all file operations mediated by class filebuf through an associated
  29284. FILE object. The member object _File points at the associated object, or
  29285. stores a null pointer if none exists. The secret member function _Init,
  29286. described below, initializes a filebuf object. Its arguments specify the
  29287. initial values stored in the _File member objects.
  29288. The type _Uninitialized, and the value _Noinit, mark the constructor used to
  29289. perform some "double constructor" magic. The standard streams make use of this
  29290. magic so they can be usable even within static constructors and destructors
  29291. for other objects. (See "Standard C: The Header <ios>," CUJ, May 1994.)
  29292. Note that class stdiobuf is based on class filebuf. The draft C++ Standard
  29293. says that stdiobuf is derived directly from streambuf. I pulled a similar
  29294. trick in implementing the header <sstream>, deriving stringbuf from
  29295. strstreambuf. (See "Standard C: The Header <sstream>," CUJ, March 1995.) As
  29296. before, such indirect derivation is permitted by the library "front matter."
  29297. A fundamental difference exists between the classes filebuf and stdiobuf,
  29298. however. Destroying a filebuf object closes any associated file. Destroying a
  29299. stdiobuf object does not. I added the member object _Closef to filebuf to tell
  29300. its destructor what to do. The stored value is nonzero only if the file is to
  29301. be closed when the object is destroyed. That only happens after filebuf::open
  29302. successfully opens a file.
  29303.  
  29304.  
  29305. Improving Performance
  29306.  
  29307.  
  29308. Listing 2 shows the file filebuf.c, which defines a number of functions
  29309. required for practically any use of class filebuf. On the face of it, this
  29310. implementation errs strongly on the side of portability, at the cost of
  29311. performance. All of the member functions defined here will work atop any
  29312. Standard C library. Moreover, none of the functions buffer reads or writes,
  29313. beyond whatever buffering that may occur in the associated FILE object.
  29314. Before you dismiss this as a purely tutorial implementation of class filebuf,
  29315. take a closer look at the last function definition in the file. As I mentioned
  29316. above, the member function _Init initializes all filebuf objects when they are
  29317. constructed. It also reinitializes an object after a successful call to
  29318. filebuf::open. And it can be made to do one very important additional thing.
  29319. As I indicated way back in June 1994, I defined the base class streambuf from
  29320. the outset with a bit of extra flexibility. The six pointers that control
  29321. in-memory buffers are all indirect pointers. You can point them at pointers
  29322. within the streambuf object itself, or at pointers in another object. For the
  29323. derived class filebuf, you can sometimes choose the latter course to
  29324. advantage. That's why the macro_HAS_PJP_CLIB chooses between two different
  29325. calls to streambuf::_Init, which initializes all those direct and indirect
  29326. pointers in the base subobject.
  29327. An arbitrary Standard C library should contain no definition for this macro.
  29328. The code works correctly, if not as fast as many would like. But for operation
  29329. atop my implementation of the Standard C library [1], you can do much better.
  29330. Define the macro _HAS_PJP_CLIB and the indirect pointers are set differently.
  29331. They point at the pointers stored in the FILE object controlling access to the
  29332. file. The pointer discipline is similar enough for things to work properly.
  29333. Here's why. I designed the FILE structure in C so that the macros getchar and
  29334. putchar could expand to inline code that is reasonably small and generally
  29335. very fast. The input and output streams are each controlled by a triple of
  29336. pointers to characters. The C++ equivalent of the macro getchar, for example,
  29337. is the inline function definition:
  29338. inline int getchar() {
  29339. return ((_Files[0]->_ Next <
  29340. _Files [0]->_Rend
  29341. ? *_Files[0]->_Next++:
  29342. fgetc(_Files[0])));
  29343. }
  29344. Compare this code with the definition of the similar streambuf public member
  29345. function sgetc:
  29346. int sgetc() {
  29347. return (gptr() != 0 &&
  29348. gptr() < egptr()
  29349. ? *_Gn() : underflow());
  29350. }
  29351. The only real difference in protocol is a small one. getchar can assume that
  29352. its "next" pointer _Files[0]->_Next is never a null pointer. It certainly
  29353. doesn't hurt for sgetc to make the extra test.
  29354. What typically happens when extracting from an input stream is pretty much
  29355. what you'd hope for. If the buffer is empty, sgetc calls underflow. The
  29356. overriding definition in class filebuf rediscovers that the buffer is empty
  29357. and calls fgetc to supply a single character. Fortunately, fgetc often
  29358. delivers up a whole buffer full of additional characters in the bargain.
  29359. The next several hundred calls to sgetc simply exercise inline code that
  29360. accesses the stored character value directly from the buffer and updates the
  29361. "next" pointer to note its consumption. This is the same pointer as is used by
  29362. getchar, either as a macro or an inline function definition. It is also, of
  29363. course, the same pointer as is used by fgetc. Thus, tight synchronization is
  29364. maintained across all flavors of input. Equally important, many character
  29365. extractions within istream extractors have no function-call overhead
  29366. whatsoever.
  29367. Of course, the same rules apply to out-put streams. Most characters inserted
  29368. by streambuf::sputc get stored directly into the output buffer, with the
  29369. "next" pointer suitably updated. Thus, many character insertions within
  29370. ostream inserters also avoid function-call overhead. Quod erat demonstrandum.
  29371. Any implementation of the Standard C library that follows this discipline for
  29372. FILE pointers can benefit from the same performance improvement. You probably
  29373. have to change the pointer member names in the definition of filebuf::_Init.
  29374. Nothing else need change, however.
  29375.  
  29376.  
  29377.  
  29378. The Remaining Code
  29379.  
  29380.  
  29381. Listing 3 shows the file fiopen.c, which shows the member function
  29382. filebuf::open. It maps the mode argument, of type openmode, to the equivalent
  29383. mode string expected by the function fopen. If that function succeeds in
  29384. opening the file, it reinitializes the filebuf object to control the
  29385. associated FILE object.
  29386. All the remaining source files needed to implement the header <fstream> are
  29387. trivial. Listing 4 shows the file ifstream.c, which defines the destructor for
  29388. class ifstream. Listing 5 shows the file ofstream.c, which defines the
  29389. destructor for class ofstream. Listing 6 shows the file stdiobuf.c, which
  29390. defines the destructor for class stdiobuf. Listing 7 shows the file
  29391. istdiost.c, which defines the destructor for class istdiostream. And Listing 8
  29392. shows the file ostdiost.c, which defines the destructor for class
  29393. ostdiostream.
  29394.  
  29395.  
  29396. Testing <fstream>
  29397.  
  29398.  
  29399. Listing 9 shows the file tfstream.c. It tests the basic properties of the
  29400. classes defined in <fstream>. It does so by manipulating a temporary file
  29401. whose name is obtained by calling tmpnam, declared in <stdio.h>. First it
  29402. tries to write the file, then read from it, then intermix reads and writes. It
  29403. also performs some modest file-positioning operations along the way. Finally,
  29404. it repeats a few of these operations using the classes istdiostream and
  29405. ostdiostream.
  29406. If all goes well, the program prints:
  29407. SUCCESS testing <fstream>
  29408. and takes a normal exit. It also removes the temporary file it created.
  29409. References
  29410. [1] P.J. Plauger, The Standard C Library, (Englewood Cliffs, N.J.:
  29411. Prentice-Hall, 1992).
  29412. This article is excerpted in part from P.J. Plauger, The Draft Standard C++
  29413. Library, (Englewood Cliffs, N.J.: Prentice-Hall, 1995).
  29414.  
  29415. Listing 1 The file fstream
  29416. // fstream standard header
  29417. #ifndef _FSTREAM_____LINEEND____
  29418. #define _FSTREAM_____LINEEND____
  29419. #include <istream>
  29420. #include <ostream>
  29421. // class filebuf
  29422. struct _Filet;
  29423. class filebuf: public streambuf {
  29424. public:
  29425. filebuf(_Filet *_F = 0)
  29426. { _Init(_F); }
  29427. filebuf(ios::_Uninitialized)
  29428. : streambuf(ios::_Noinit) {}
  29429. virtual ~filebuf();
  29430. bool is_open() const
  29431. {return ((_File !: 0)); }
  29432. filebuf *open(const char *, ios::openmode);
  29433. filebuf *open(const char *_N, ios::open_mode _M)
  29434. {return (open(_N, (ios::openmode)_M)); }
  29435. filebuf *close();
  29436. protected:
  29437. virtual int overflow(int: EOF);
  29438. virtual int pbackfail(int = EOF);
  29439. virtual int underflow();
  29440. virtual int uflow();
  29441. virtual streamsize xsgetn(char *, streamsize);
  29442. virtual streamsize xsputn(const char *, streamsize);
  29443. virtual streampos seekoff(streamoff, ios::seekdir,
  29444. ios::openmode = (ios::openmode)(ios::in ios::out));
  29445. virtual streampos seekpos(streampos,
  29446. ios::openmode = (ios::openmode)(ios::in ios::out));
  29447. virtual streambuf *setbuf(char *, streamsize);
  29448. virtual int sync();
  29449. _Filet *_Init(_Filet * = 0, bool = 0);
  29450. private:
  29451. bool _Closef;
  29452. _Filet *_File;
  29453. };
  29454. // class ifstream
  29455. class ifstream: public istream {
  29456.  
  29457. public:
  29458. ifstream()
  29459. : istream(&_Fb) {}
  29460. ifstream(const char *_S, openmode _M = in)
  29461. : istream(&_Fb) {_Fb.open(_S, _M); }
  29462. virtual ~ifstream();
  29463. filebuf *rdbuf() const
  29464. {return ((filebuf *)&_Fb); }
  29465. bool is_open() const
  29466. {return (_Fb.is_open()); }
  29467. void open(const char *_S, openmode _M = in)
  29468. {if (_Fb.open(_S, _M) == 0)
  29469. setstate(failbit); }
  29470. void open(const char *_S, open_mode _M)
  29471. {open(_S, (openmode)_M); }
  29472. void close()
  29473. {if (_Fb.close() == 0)
  29474. setstate(failbit); }
  29475. private:
  29476. filebuf _Fb;
  29477. };
  29478. // class ofstream
  29479. class ofstream: public ostream {
  29480. public:
  29481. ofstream()
  29482. : ostream(&_Fb) {}
  29483. ofstream(const char *_S, openmode _M = out trunc)
  29484. : ostream(&_Fb) {_Fb.open(_S, _M; }
  29485. virtual ~ofstream();
  29486. filebuf *rdbuf() const
  29487. {return ((filebuf *)&_Fb); }
  29488. bool is_open() const
  29489. {return (_Fb.is_open()); }
  29490. void open(const char *_S, openmode_M = out trunc)
  29491. {if (_Fb.open(_S, _M) == 0)
  29492. setstate(failbit); }
  29493. void open(const char *_S, open_mode _M)
  29494. {open(_S, (openmode)_M); }
  29495. void close()
  29496. {if (_Fb.close() == 0)
  29497. setstate(failbit); }
  29498. private:
  29499. filebuf _Fb;
  29500. };
  29501. // class stdiobuf
  29502. class stdiobuf : public filebuf {
  29503. public:
  29504. stdiobuf(_Filet *_F)
  29505. : filebuf(_F), _Is_buffered(0) {}
  29506. virtual ~stdiobuf();
  29507. bool buffered() const
  29508. {return (_Is_buffered); }
  29509. void buffered(bool _F)
  29510. {_Is_buffered = _F; }
  29511. private:
  29512. bool _Is_buffered;
  29513. };
  29514. // class istdiostream
  29515. class istdiostream: public istream {
  29516.  
  29517. public:
  29518. istdiostream(_Filet *_F)
  29519. : istream(&_Fb), _Fb(_F) {}
  29520. virtual ~istdiostream();
  29521. stdiobuf *rdbuf() const
  29522. {return ((stdiobuf *)&_Fb); }
  29523. bool buffered() const
  29524. {return (_Fb.buffered()); }
  29525. void buffered(bool_F)
  29526. {_Fb.buffered(_F); }
  29527. private:
  29528. stdiobuf _Fb;
  29529. };
  29530. // class ostdiostream
  29531. class ostdiostream: public ostream {
  29532. public:
  29533. ostdiostream(_Filet *_F)
  29534. : ostream(&_Fb), _Fb(_F) {}
  29535. virtual ~ostdiostream();
  29536. stdiobuf *rdbuf() const
  29537. {return ((stdiobuf *)& _Fb); }
  29538. bool buffered() const
  29539. {return (_Fb.buffered()); }
  29540. void buffered(bool _F)
  29541. {_Fb.buffered(_F); }
  29542. private:
  29543. stdiobuf _Fb;
  29544. };
  29545. #endif /* _FSTREAM_ */
  29546.  
  29547.  
  29548. Listing 2 The file filebuf.c
  29549. //filebuf -- filebuf basic members
  29550. #include <stdio.h>
  29551. #include <fstream>
  29552.  
  29553. filebuf::~filebuf()
  29554. { // destruct a filebuf
  29555. if (_Closef)
  29556. close();
  29557. }
  29558.  
  29559. filebuf *filebuf::close()
  29560. { // close a file
  29561. if (_File != 0 && fclose(_File) == 0)
  29562. { // note successful close
  29563. _Init();
  29564. return (this);
  29565. }
  29566. else
  29567. return (0);
  29568. }
  29569.  
  29570. int filebuf::overflow(int ch)
  29571. { // try to write output
  29572. return (pptr() != 0 && pptr() < epptr()
  29573. ? (*_Pn()++ = ch)
  29574. : _File == 0 ? EOF: ch == EOF ? 0: fputc(ch, _File));
  29575. }
  29576.  
  29577.  
  29578. int filebuf::pbackfail(int ch)
  29579. { // try to pushback a character
  29580. return (gptr() != 0 && eback() < gptr() && ch == gptr()[-1]
  29581. ? *--_Gn()
  29582. : _File == 0 ch == EOF ? EOF : ungetc(ch, _File));
  29583. }
  29584.  
  29585. int filebuf::underflow()
  29586. { // try to peek at input
  29587. return (gptr() != 0 && gptr() < egptr()
  29588. ? *_Gn()
  29589. :_File == 0 ? EOF : ungetc(fgetc(_File),_File));
  29590. }
  29591.  
  29592. int filebuf::uflow()
  29593. { // try to consume input
  29594. return (gptr() != 0 && gptr() < egptr()
  29595. ? *_Gn()++
  29596. : _File == 0 ? EOF: fgetc(_File));
  29597. }
  29598.  
  29599. streamsize filebuf::xsgetn(char *s, streamsize n)
  29600. { // read n characters
  29601. return (_File == 0 ? 0: fread(s, 1, n,_File));
  29602. }
  29603.  
  29604. streamsize filebuf::xsputn(const char *s, streamsize n)
  29605. { // write n characters
  29606. return (_File == 0 ? 0 : fwrite(s, 1, n,_File));
  29607. }
  29608.  
  29609. streampos filebuf::seekoff(streamoff off, ios::seekdir way,
  29610. ios::openmode)
  29611. { // seek by specified offset
  29612. return (streampos(_File == 0
  29613.  fseek(_File, off, way) != 0
  29614. ? _BADOFF : streamoff(ftell(_File))));
  29615. }
  29616.  
  29617. streampos filebuf::seekpos(streampos sp, ios::openmode)
  29618. { // seek to memorized position
  29619. return (_File == 0 fsetpos(_File, sp._Fpos()) != 0
  29620.  fseek(_File, sp.offset(), SEEK_CUR) != 0
  29621.  fgetpos(_File, sp._Fpos()) != 0
  29622. ? streampos(_BADOFF)
  29623. : streampos(0, sp._Fpos()));
  29624. }
  29625.  
  29626. streambuf *filebuf::setbuf(char *s, streamsize n)
  29627. { // provide a file buffer
  29628. return (_File == 0 setvbuf(_File, s, _IOFBF, n) != 0
  29629. ? 0: this);
  29630. }
  29631.  
  29632. int filebuf::sync()
  29633. { // synchronize buffer with file
  29634. return (_File == 0 ? 0 : fflush(_File));
  29635. }
  29636.  
  29637.  
  29638. FILE *filebuf::_Init(FILE *fp, bool closef)
  29639. { // initialize buffer pointers
  29640. #if _HAS_PJP_CLIB
  29641. if (fp == 0)
  29642. streambuf::_Init();
  29643. else
  29644. streambuf::_Init((char **)&fp->_Buf,
  29645. (char **)&fp->_Next,
  29646. (char **)&fp->_Rend,
  29647. (char **)&fp->_Buf,
  29648. (char **)&fp->_Next,
  29649. (char **)&fp->_Wend);
  29650. #else
  29651. streambuf::_Init();
  29652. #endif
  29653. _Closef = closef;
  29654. _File = fp;
  29655. return (_File);
  29656. }
  29657.  
  29658. REF_DEST 9505c01.s3Listing 3 The file fiopen.c
  29659. // fiopen -- filebuf::open(const char *, ios::openmode)
  29660. #include <stdio.h>
  29661. #include <fstream>
  29662.  
  29663. filebuf *filebuf::open(const char *name, ios::openmode mode)
  29664. { // open a file
  29665. static const char *mods[]: {
  29666. "r", "w", "a", "rb", "wb", "ab", "r+", "w+", "a+",
  29667. "r+b", "w+b", "a+b", 0};
  29668. static const int valid[] = {
  29669. ios::in, ios::outios::trunc, ios::outios::app,
  29670. ios::inios::binary, ios::outios::truncios::binary,
  29671. ios::outios::appios::binary, ios::inios::out,
  29672. ios::inios::outios::trunc, ios::inios::outios::app,
  29673. ios::inios::outios::binary,
  29674. ios::inios::outios::truncios::binary,
  29675. ios::inios::outios::appios::binary, 0};
  29676. FILE *fp;
  29677. int n;
  29678. ios::openmode atefl = mode & ios::ate;
  29679. if (_File != 0)
  29680. return (0);
  29681. mode &= ~ios::ate;
  29682. for (n = 0; valid[n] !: 0 && valid[n] !: mode; ++n)
  29683. ;
  29684. if (valid[n] == 0 (fp = fopen(name, mods[n])) == 0)
  29685. return (0);
  29686. if (!atefl fseek(fp, 0, SEEK_END) == 0)
  29687. { // success, initialize and return
  29688. _Init(fp. 1);
  29689. return (this);
  29690. }
  29691. fclose(fp); // can't position at end
  29692. return (0);
  29693. }
  29694.  
  29695.  
  29696.  
  29697. Listing 4 The file ifstream.c
  29698. // ifstream -- ifstream basic members
  29699. #include <fstream>
  29700.  
  29701. ifstream::~ifstream( )
  29702. { // destruct an ifstream
  29703. }
  29704.  
  29705.  
  29706. Listing 5 The file ofstream.c
  29707. // ofstream -- ofstream basic members
  29708. #include <fstream>
  29709.  
  29710. ofstream::~ofstream()
  29711. { // destruct an ofstream
  29712. }
  29713.  
  29714.  
  29715. Listing 6 The file stdiobuf.c
  29716. // stdiobuf -- stdiobuf basic members
  29717. #include <fstream>
  29718.  
  29719. stdiobuf::~stdiobuf()
  29720. { // destruct a stdiobuf
  29721. }
  29722.  
  29723.  
  29724. Listing 7 The file istdiost.c
  29725. // istdiostream -- istdiostream basic members
  29726. #include <fstream>
  29727.  
  29728. istdiostream::~istdiostream()
  29729. { // destruct an istdiostream
  29730. }
  29731.  
  29732.  
  29733. Listing 8 The file ostdiost.c
  29734. //ostdiostream -- ostdiostream basic members
  29735. #include <fstream>
  29736.  
  29737. ostdiostream::~ostdiostream()
  29738. { // destruct an ostdiostream
  29739. }
  29740.  
  29741.  
  29742. Listing 9 The file tfstream.c
  29743. // test <fstream>
  29744. #include <cassert>
  29745. #include <cstdio>
  29746. #include <cstring>
  29747. #include <fstream>
  29748. #include <iostream>
  29749.  
  29750. int main()
  29751. { // test basic workings of fstream definitions
  29752. ifstream ifs;
  29753. ofstream ofs;
  29754. const char *tn = tmpnam(NULL);
  29755. assert(tn != NULL);
  29756.  
  29757. // test closed file closing
  29758. assert(!ifs.is_open() && !ifs.fail());
  29759. ifs.close();
  29760. assert(ifs.fail() && ifs.rdbuf()->close() == 0);
  29761. assert(!ofs.is_open() && !ofs.fail());
  29762. ofs.close();
  29763. assert(ofs.fail() && ofs.rdbuf()->close() == 0);
  29764. // test output file operations
  29765. ofs.clear(), ofs.open(tn, ios::out ios::trunc);
  29766. assert(ofs.is_open() && ofs.rdbuf()->is_open());
  29767. ofs << "this is a test" << endl;
  29768. ofs.close();
  29769. assert(!ofs.is_open() && ofs.good());
  29770. assert(ofs.rdbuf()->open(tn, ios::app ios::out) != 0;
  29771. ofs << "this is only a test" << endl;
  29772. ofs.close();
  29773. assert(!ofs.is_open() && ofs.good());
  29774. // test input file operations
  29775. char buf[50];
  29776. ifs.clear(), ifs.open(tn, ios::in);
  29777. assert(ifs.is_open() && ifs.rdbuf()->is_open());
  29778. ifs.getline(buf, sizeof (buf));
  29779. assert(strcmp(buf, "this is a test") == 0);
  29780. streampos p1 = ifs.rdbuf()->pubseekoff(0, ios::cur);
  29781. ifs.getline(buf, sizeof (buf));
  29782. assert(strcmp(buf, "this is only a test") == 0);
  29783. assert(ifs.rdbuf()->pubseekpos(p1) == p1);
  29784. ifs.getline(buf, sizeof (buf));
  29785. assert(strcmp(buf, "this is only a test") == 0);
  29786. ifs.rdbuf()->pubseekoff(0, ios::beg);
  29787. ifs.getline(buf, sizeof (buf));
  29788. assert(strcmp(buf. "this is a test") == 0);
  29789. ifs.close();
  29790. assert(!ifs.is_open() && ifs.good());
  29791. // test combined file operations
  29792. ifstream nifs(tn, ios::in ios::out);
  29793. ostream nofs(nifs.rdbuf());
  29794. assert(nifs.is_open() && nifs.good() && nofs.good());
  29795. nifs.rdbuf()->pubseekoff(0, ios::end);
  29796. nofs << "this is still just a test" << endl;
  29797. nifs.rdbuf()->pubseekoff(0, ios::beg);
  29798. nifs.getline(buf, sizeof (buf));
  29799. assert(strcmp(buf, "this is a test") == 0);
  29800. nifs.getline(buf, sizeof (buf));
  29801. assert(strcmp(buf, "this is still just a test") == 0);
  29802. nifs.getline(buf, sizeof (buf));
  29803. assert(strcmp(buf, "this is only a test") == 0);
  29804. nifs.close();
  29805. ofstream nnofs(tn,
  29806. ios::in ios::out ios::ate);
  29807. assert(nnofs.is_open());
  29808. nnofs << "one last test" << endl;
  29809. nnofs.close();
  29810. // test stdiobuf operations
  29811. FILE *fi = fopen(tn. "r+");
  29812. { // bound lifetime of istd and ostd
  29813. istdiostream istd(fi);
  29814. ostdiostream ostd(fi);
  29815. assert(fi != 0);
  29816.  
  29817. assert(istd.buffered() == 0
  29818. && istd.rdbuf()->buffered() == 0);
  29819. istd.rdbuf()->buffered(0), istd.buffered(1);
  29820. assert(istd.buffered() != 0
  29821. && istd.rdbuf()->buffered() != 0);
  29822. assert(ostd.buffered() == 0
  29823. && ostd.rdbuf()->buffered() == 0);
  29824. ostd.rdbuf()->buffered(0), ostd.buffered(1);
  29825. assert(ostd.buffered() != 0
  29826. && ostd.rdbuf()->buffered() != 0);
  29827. istd.getline(buf, sizeof (buf));
  29828. assert(strcmp(buf, "this is a test") == 0);
  29829. p1 = istd.rdbuf()->pubseekoff(0, ios::end);
  29830. ostd << "still one more last test" << end1;
  29831. assert(ostd.rdbuf()->pubseekpos(p1) == p1);
  29832. istd.getline(buf, sizeof (buf));
  29833. assert(strcmp(buf, "still one more last test") == 0);
  29834. }
  29835. assert(fclose(fi) == 0 && remove(tn) == 0);
  29836. cout << "SUCCESS testing <fstream>" << end1;
  29837. return (0);
  29838. }
  29839.  
  29840.  
  29841.  
  29842.  
  29843.  
  29844.  
  29845.  
  29846.  
  29847.  
  29848.  
  29849.  
  29850.  
  29851.  
  29852.  
  29853.  
  29854.  
  29855.  
  29856.  
  29857.  
  29858.  
  29859.  
  29860.  
  29861.  
  29862.  
  29863.  
  29864.  
  29865.  
  29866.  
  29867.  
  29868.  
  29869.  
  29870.  
  29871.  
  29872.  
  29873.  
  29874.  
  29875.  
  29876.  
  29877.  
  29878.  
  29879.  
  29880. Code Capsules
  29881.  
  29882.  
  29883. Data Abstraction
  29884.  
  29885.  
  29886.  
  29887.  
  29888. Chuck Allison
  29889.  
  29890.  
  29891. Chuck Allison is a regular columnist with CUJ and a Senior Software Engineer
  29892. in the Information and Communication Systems Department of the Church of Jesus
  29893. Christ of Latter Day Saints in Salt Lake City. He has a B.S. and M.S. in
  29894. mathematics, has been programming since 1975, and has been teaching and
  29895. developing in C since 1984. His current interest is object-oriented technology
  29896. and education. He is a member of X3J16, the ANSI C++ Standards Committee.
  29897. Chuck can be reached on the Internet at 72640.1507@compuserve.com.
  29898.  
  29899.  
  29900. You may be tired of hearing it, but let me say it one last time: you can view
  29901. C++ in three ways: 1) as a better C, 2) as a language that supports data
  29902. abstraction, and 3) as a vehicle for object-oriented programming. In last
  29903. month's article I explored the "better C" features of C++. This month I'll
  29904. show how it supports data abstraction, or, in other words, how it helps you
  29905. create new data types.
  29906.  
  29907.  
  29908. Abstraction
  29909.  
  29910.  
  29911. Unlike a machine, the human mind can handle only a very limited amount of
  29912. complexity. Life is inherently complex, and so are software systems that model
  29913. reality. To master a complex system, it is necessary to focus on only the few
  29914. things that matter most in a given context, and ignore the rest. This process,
  29915. called abstraction, enables system designers to solve complex problems in an
  29916. organized, manageable way. Grady Booch defines an abstraction as follows:
  29917. "An abstraction denotes the essential characteristics of an object that
  29918. distinguish it from all other kinds of objects and thus provide
  29919. crisply-defined conceptual boundaries, relative to the perspective of the
  29920. viewer." [1]
  29921. Abstraction often manifests itself in software through user-defined data
  29922. types, new classes of objects composed of built-in or other user-defined
  29923. types. This concept is not new, of course, but languages that support it well,
  29924. such as Ada, C++, CLOS, and Smalltalk, have only recently seen widespread use.
  29925. If you've been programming in C for any length of time, you've probably used
  29926. the struct mechanism. You've also had to provide functions to process those
  29927. structures. Whether you knew it or not, you were creating a user-defined type.
  29928. Chances are, for example, you've needed to handle dates in a program. Listing
  29929. 1 defines a struct Date and two functions for processing dates, one to format
  29930. a date as Month Day, Year, and one to compare dates. (See Listing 2 for the
  29931. functions' implementation and Listing 3 for a test program.)
  29932. To use this abstract data type, you only need to know the protocol for its two
  29933. functions, which constitute its interface. You don't need to know how the
  29934. functions were implemented, or even the structure layout. A well-defined
  29935. abstraction enables concentration on the outside view while ignoring
  29936. implementation details. This barrier between interface and implementation is
  29937. called encapsulation. There is a hole in type Date's encapsulation barrier, of
  29938. course, because it's possible to bypass the interface and directly manipulate
  29939. one of the structure members, as in
  29940. dp->month = 7;
  29941. That hole can be closed up, since the interface itself doesn't need to know
  29942. anything about a Date, it just needs a pointer to it. In Listing 4 I define
  29943. Date as an incomplete type -- i.e., I just state that it exists and nothing
  29944. more. A client program sees only date2.h, and therefore knows nothing about a
  29945. Date's layout or implementation. I complete the type in the implementation in
  29946. Listing 5 (the test program is in Listing 6). This extra measure of protection
  29947. costs something, however. Programs using Date must now call explicit create
  29948. and destroy functions, and must access all Date objects through pointers only.
  29949. C++ improves support for user-defined data types in at least two ways: 1) by
  29950. allowing definition of the interface functions within the scope of the class,
  29951. and 2) by barring client access to implementation details via a language
  29952. keyword, as opposed to the incomplete type trick. The Date class in Listing 7
  29953. and Listing 8 differs from that in Listing 1 and Listing 2 in the following
  29954. ways:
  29955. 1) The data members are private, so client programs can't access them directly
  29956. (only the member functions can).
  29957. 2) The interface functions are public member functions, so they can only apply
  29958. to Date objects, and the function names do not pollute the global namespace.
  29959. The date_ prefix is no longer necessary, since the operations belong to the
  29960. class. Note that format and compare are const member functions, meaning that
  29961. they promise not to alter a Date's data members.
  29962. 3) A constructor replaces C's structure initialization syntax.
  29963. 4) The text representations for the month names now appear in the scope of
  29964. struct Date (being static members).
  29965. The sample program in Listing 9 shows that member functions are invoked with
  29966. the usual dot operator for structure members.
  29967. If it bothers you that a client programmer can still see the layout of your
  29968. data members -- albeit without direct access -- you can hide the data member
  29969. declarations in a new incomplete type, but this time with absolutely no impact
  29970. to the interface (see Listing 10 and Listing 11). To demonstrate this, I
  29971. define the DateRep helper class with a constructor, and for efficiency and
  29972. simplicity I give the Date class direct access to the its data members by
  29973. making it a friend to DateRep. The Date constructor now needs to create its
  29974. associated DateRep object on the heap, so a destructor is needed to free the
  29975. heap memory. Only the header file has to change in the test program in Listing
  29976. 9. With a well-defined abstraction, a change in implementation will not
  29977. inflict change on client code.
  29978.  
  29979.  
  29980. Operator Overloading
  29981.  
  29982.  
  29983. You can make user-defined types nearly as convenient to use as built-in types
  29984. by adding operator functions to your class definition. In Listing 12 through
  29985. Listing 14 I've added the six relational operations and a stream inserter for
  29986. easy output. For efficiency I've also defined the smaller functions inline
  29987. within the header file. Note also the declaration of ostream as an incomplete
  29988. type in the header file. Since an ostream object appears there only by
  29989. reference, date5.h does not have to be dependent on the iostream.h header,
  29990. which is one of the largest in the Standard C++ library. The implementation of
  29991. operator<< shows that defining a stream inserter usually just reduces to
  29992. inserting an object's members into the stream. I simulate the new C++ Boolean
  29993. data type, bool, with the definitions in Listing 15.
  29994. As the definition of the Person class in Listing 16 shows, you can compose a
  29995. new type from other user-defined types, as well as built-in types. Each Person
  29996. has a birth date, which is a Date object wholly contained within a Person
  29997. object. The Person constructor passes the Date information on to its Date
  29998. subobject via an initialization list. The initialization list follows the
  29999. colon in the function header, as defined in the implementation file (Listing
  30000. 17). Notice how Person::operator<< implicitly uses Date::operator<<. Class
  30001. Person also uses the new C++ standard string class for its text data members.
  30002. (Borland C++, the compiler I used for most of these examples, defines the
  30003. string class in the header (cstring.h>).
  30004.  
  30005.  
  30006. Concrete Data Types
  30007.  
  30008.  
  30009. C++ support for data abstraction, and operator overloading in particular, can
  30010. make user-defined data types as convenient to use as built-in types. What are
  30011. typical operations performed on built-in types? Programs can initialize them,
  30012. assign them to other objects of compatible type, and pass them to or receive
  30013. them back from functions. In C++, you can perform these same actions with your
  30014. own types, as long as either you or the compiler provide certain special
  30015. member functions. The presence of these member functions constitutes a
  30016. concrete data type, one that behaves intelligently wherever a built-in type
  30017. does. These functions include the following:
  30018. copy constructor
  30019. default constructor (and
  30020. others as needed)
  30021. assignment operator
  30022. destructor
  30023. Whenever an object is created, a constructor is called to initialize it. The
  30024. initializer arguments must match a constructor's parameters in number and
  30025. type. The default constructor is the one that takes no arguments. Another
  30026. special constructor, called the copy constructor, initializes a new object
  30027. from an existing object of the same type, and has the signature T(const T&),
  30028. or T(T&), for some type T. This constructor executes whenever you pass or
  30029. return an object by value, or explicitly initialize a new object from an old
  30030. one, as with y in the following example:
  30031. T x;
  30032. ...
  30033. T y = x; // same as "T y(x)"
  30034. The assignment operator executes whenever you assign the value of an object to
  30035. an existing one, and has the signature
  30036.  
  30037. T& T::operator=(const T&)
  30038. Note the assignment statement in Listing 18 (p3 = p2;). I did not explicitly
  30039. define Person::operator=(), but it seems to have worked anyway. This is
  30040. because the compiler generated it for me. The absence of operator=() in the
  30041. class definition caused the compiler to generate one that does memberwise
  30042. assignment, like this:
  30043. Person& Person::operator=(const Person& p)
  30044. {
  30045. last = p.last;
  30046. first = p.first;
  30047. birth = p.birth;
  30048. ssn = p.ssn;
  30049. return *this;
  30050. }
  30051. Similarly, if you do not supply a copy constructor, the compiler generates
  30052. this one:
  30053. Person::Person(const Person& p)
  30054. : last(p.last), first(p.first), birth(p.birth),
  30055. ssn(p.ssn)
  30056. {}
  30057. which, as you can see, calls the copy constructor for each member object.
  30058. If you define any constructor other than a copy constructor, then the compiler
  30059. will not generate a default constructor for you. If you don't define any
  30060. constructors (other than copy constructors) the compiler will generate a
  30061. default constructor which will invoke the corresponding default constructor
  30062. for all user-defined member objects. A compiler-generated destructor calls the
  30063. destructors associated with any user-defined member objects.
  30064. The compiler-generated assignment operator and copy constructor for Person
  30065. worked because the standard string class provides its own version of these two
  30066. functions, and because the Data class data members are built-in, so its
  30067. compiler-generated constructors and operators suffice. In general, whenever
  30068. the state of an object is contained entirely within the object itself, you do
  30069. not need to override the compiler-generated member functions.
  30070. The Person class in Listing 19 and Listing 20 stores its text data on the
  30071. heap. A compiler-generated assignment operator or copy constructor will only
  30072. copy pointers to the receiving object, when what it really needs to do is
  30073. allocate space on the heap for the duplicated text. When I execute the test
  30074. program on this class, Metaware's C++ gives the following output on Windows
  30075. NT:
  30076. p1 == {Richardson,Alice,[December 16, 1947], 123-45-6789}
  30077. p2 == {Doe,John,[Bad month 0, 0],}
  30078. p1 does not equal p2
  30079. p3 does equal p2
  30080. ***Free(0X4C105C):X POINTER already free
  30081. ABORTING. . .
  30082. At program exit, the compiler calls the Person destructor to destroy p3, which
  30083. frees the text memory on the heap. Since p2 points to the same memory, the
  30084. destructor attempts to delete it a second time when destroying p2, which is an
  30085. error. Listing 21 and Listing 22 fix the problem by adding the missing member
  30086. functions. The assignment operator uses the clever-but-lazy trick of
  30087. explicitly invoking the destructor, followed by a call to placement new, which
  30088. in this case executes the copy constructor on the current object. (For more
  30089. information on placement new, see the Code Capsule "Dynamic Memory Management,
  30090. Part II," CUJ, November 1994.)
  30091.  
  30092.  
  30093. Generic Types
  30094.  
  30095.  
  30096. In programming, one of the most useful abstractions is the kind that allows
  30097. independence from data type. An example of such an abstraction is a container.
  30098. Containers are objects that hold other objects. A set is a container that
  30099. provides operations to insert, remove, and test elements for membership.
  30100. Listing 23 - Listing 25 illustrate a class that implements a set type for
  30101. integers. It uses a fixed-size array as the underlying data structure
  30102. (although bit sets are a more efficient implementation for sets of ordinal
  30103. values). The implementation in Listing 24 uses the find algorithm, a new
  30104. addition to the Standard C++ library, which expects pointers to the beginning
  30105. and to one past the end of the array as its first two parameters. If you were
  30106. to define SetOfLong, or SetOfString, or a set for any other type supporting
  30107. the equality operator, you would find that the only thing that changed would
  30108. be the type of objects in the set. If you think about it, a set really
  30109. shouldn't have to care about the type of objects it holds.
  30110. C++'s template mechanism allows you to define a generic set type that
  30111. essentially ignores the type of the objects it contains, by specifying that
  30112. type as a parameter (see Listing 26). For efficiency, I use the
  30113. dynamically-sized vector template class from the Standard C++ library in the
  30114. implementation. Dynamic sizing is crucial when using a set of large objects,
  30115. since a fixed-size array of the same would result in much wasted space. As you
  30116. can see in set2.h in Listing 26, the find algorithm also works on vectors.
  30117. This is because its begin and end member functions return an iterator, which
  30118. is a generalization of a pointer. The erase member function expects an
  30119. iterator argument, which find returns. The program in Listing 27 uses the Set
  30120. template to hold Person objects.
  30121.  
  30122.  
  30123. Summary
  30124.  
  30125.  
  30126. Complexity is an unavoidable fact of life. Support for user-defined data types
  30127. enables the programmer to model and manage a complex system in software. C++
  30128. supports data abstraction via classes, member access control, constructors and
  30129. destructors, operator functions, and templates. If you provide the suitable
  30130. member functions, you can define abstractions that have the convenience of
  30131. built-in data types.
  30132.  
  30133.  
  30134. Computers In Kansas
  30135.  
  30136.  
  30137. During the last couple of years I've enjoyed wearing the official T-shirt of
  30138. C/C++ Users Journal. On the back it says, "Yes, there really are computers in
  30139. Kansas." Not only are there computers, but there is a group of talented and
  30140. pleasant individuals who comprise R&D Publications. In January they sponsored
  30141. "C/C++ Solutions '95," a practical, "how-to" seminar in Kansas City.
  30142. (Missouri. All right, it's not in Kansas, but it's close.) We had ten of the
  30143. best C/C++ developers and teachers you will find anywhere, including Scott
  30144. Meyers, author of "Effective C++," and our own Dan Saks. The balance of
  30145. material and the emphasis on C as well as C++ was very well received. Sorry if
  30146. you missed it. Stay tuned -- we'll do it again. You won't get this type of
  30147. intensive training at any other seminar.
  30148. I've also enjoyed contributing as a columnist for this journal since October
  30149. 1992. This has been a valuable outlet for me to share some of my learnings as
  30150. a C/C++ developer and contributor to the C++ standard. Due to personal time
  30151. constraints, I reluctantly announce that this is my last column. I've
  30152. appreciated your feedback and support, and will miss you and the folks at R&D.
  30153. Goodbye, farewell, and keep on hacking.
  30154. Reference
  30155. [1] Grady Booch, Object-oriented Analysis and Design with Applications, Second
  30156. Edition (Benjamin-Cummings, 1994).
  30157.  
  30158. Listing 1 A Date type in C
  30159. /* date.h */
  30160.  
  30161. struct Date
  30162. {
  30163. int month;
  30164. int day;
  30165. int year;
  30166. };
  30167. typedef struct Date Date;
  30168.  
  30169.  
  30170. char *date_format(const Date *, char *);
  30171. int date_compare(const Date *, const Date *);
  30172.  
  30173. /* End of File */
  30174.  
  30175.  
  30176. Listing 2 Implementation for the Date type
  30177. /* date.c */
  30178.  
  30179. #include <stdio.h>
  30180. #include "date.h"
  30181.  
  30182. static const char *month_text[] =
  30183. {"Bad month", "January", "February",
  30184. "March", "April","May", "June", "July",
  30185. "August", "September", "October",
  30186. "November", "December"};
  30187.  
  30188. char *date_format(const Date *dp, char *buf)
  30189. {
  30190. sprintf(buf,s %d, %d",
  30191. month_text[dp->month],dp->day,
  30192. dp->year);
  30193. return buf;
  30194. }
  30195.  
  30196. int date_compare(const Date *dp1,
  30197. const Date *dp2)
  30198. {
  30199. int result = dp1->year - dp2->year;
  30200. if (result == 0)
  30201. result = dp1->month - dp2->month;
  30202. if (result == 0)
  30203. result = dp1->day - dp2->day;
  30204. return result;
  30205. }
  30206.  
  30207. /* End of File */
  30208.  
  30209.  
  30210. Listing 3 Tests the Date type
  30211. /* tdate.c */
  30212. #include <stdio.h>
  30213. #include "date.h"
  30214.  
  30215. #define DATELEN 19
  30216.  
  30217. main()
  30218. {
  30219. Date d1 = {10,1,1951}, d2 = {3,7,1995};
  30220. char buf[DATELEN+1];
  30221. int cmp;
  30222.  
  30223. printf("dl == %s\n",date_format(&d1,buf));
  30224. printf("d2 == %s\n",date_format(&d2,buf));
  30225. cmp = date_compare(&d1,&d2);
  30226. printf("d1 %s d2\n",
  30227. (cmp < 0) ? "precedes"
  30228.  
  30229. : (cmp > 0) ? "follows"
  30230. : "equals");
  30231. return 0;
  30232. }
  30233.  
  30234. /* Output:
  30235. d1 == October 1, 1951
  30236. d2 == March 7, 1995
  30237. d1 precedes d2
  30238. */
  30239.  
  30240. /* End of File */
  30241.  
  30242.  
  30243. Listing 4 A safer Date type
  30244. /* date2.h */
  30245.  
  30246. /* Declare the incomplete Date type */
  30247. typedef struct Date Date;
  30248.  
  30249. Date *date_create(int, int, int);
  30250. char *date_format(const Date *, char *);
  30251. int date_compare(const Date *, const Date *);
  30252. void date_destroy(Date *):
  30253.  
  30254. /* End of File */
  30255.  
  30256.  
  30257. Listing 5 Implementation for Listing 4
  30258. /* date2.c */
  30259.  
  30260. #include <stdio.h>
  30261. #include <stdlib.h>
  30262. #include "date2.h"
  30263.  
  30264. struct Date
  30265. {
  30266. int month;
  30267. int day;
  30268. int year;
  30269. };
  30270.  
  30271. static const char *month_text[] =
  30272. {"Bad month", "January", "February",
  30273. "March", "April", "May", "June", "July",
  30274. "August", "September", "October",
  30275. "November", "December"};
  30276.  
  30277. Date *date_create(int m, int d, int y)
  30278. {
  30279. Date *dp = malloc(sizeof(Date));
  30280. if (dp == NULL)
  30281. return NULL;
  30282.  
  30283. dp->month = m;
  30284. dp->day = d;
  30285. dp->year = y;
  30286. return dp;
  30287. }
  30288.  
  30289.  
  30290. char *date_format(const Date *dp, char *buf)
  30291. {
  30292. sprintf(buf,"%s %d, %d",
  30293. month_text [dp->month],dp->day,
  30294. dp->year);
  30295. return buf;
  30296. }
  30297.  
  30298. int date_compare(const Date *dp1,
  30299. const Date *dp2)
  30300. {
  30301. int result = dp1->year - dp2->year;
  30302. if (result == 0)
  30303. result = dp1->month - dp2->month;
  30304. if (result == 0)
  30305. result = dp1->day - dp2->day;
  30306. return result;
  30307. }
  30308.  
  30309. void date_destroy(Date *dp)
  30310. {
  30311. free(dp);
  30312. }
  30313. /* End of File */
  30314.  
  30315.  
  30316. Listing 6 Tests the new Date type
  30317. /* tdate2.c */
  30318. #include <stdio.h>
  30319. #include "date2.h"
  30320.  
  30321. #define DATELEN 19
  30322.  
  30323. main()
  30324. {
  30325. Date *d1 = date_create(10,1,1951),
  30326. *d2 = date_create(3,7,1995);
  30327. char buf[DATELEN+1];
  30328. int cmp;
  30329.  
  30330. printf("d1 == %s\n",date_format(d1,buf));
  30331. printf("d2 == %s\n",date_format(d2,buf)):
  30332. cmp = date_compare(d1,d2);
  30333. printf("d1 %s d2\n",
  30334. (cmp < 0) ? "precedes"
  30335. : (cmp > 0) ? "follows"
  30336. : "equals");
  30337. date_destroy(d1);
  30338. date_destroy(d2);
  30339. return 0;
  30340. }
  30341.  
  30342. /* Output:
  30343. d1 == October 1, 1951
  30344. d2 == March 7, 1995
  30345. d1 precedes d2
  30346. */
  30347.  
  30348.  
  30349. /* End of File */
  30350.  
  30351.  
  30352. Listing 7 The Date type in C++
  30353. // date3.h
  30354.  
  30355. struct Date
  30356. {
  30357. Date(int, int, int);
  30358. char *format(char *) const;
  30359. int compare(const Date &) const;
  30360.  
  30361. private:
  30362. int month;
  30363. int day;
  30364. int year;
  30365.  
  30366. static const char * month_text[13];
  30367. };
  30368.  
  30369. /* End of File */
  30370.  
  30371.  
  30372. Listing 8 Implementation for the Date class
  30373. // date3.cpp
  30374.  
  30375. #include <stdio.h>
  30376. #include "date3.h"
  30377.  
  30378. const char * Date::month_text[13] =
  30379. {"Bad month", "January", "February",
  30380. "March", "April", "May", "June",
  30381. "July", "August", "September",
  30382. "October", "November", "December"};
  30383.  
  30384. Date::Date (int m, int d, int y) :
  30385. month(m), day(d), year(y)
  30386. {}
  30387.  
  30388. char * Date::format(char *bur) const
  30389. {
  30390. sprintf(buf,"%s %d %d",
  30391. month_text[month],day,year);
  30392. return buf;
  30393. }
  30394.  
  30395. int Date::compare(const Date & dp2) const
  30396. {
  30397. int result = year - dp2.year;
  30398. if (result == 0)
  30399. result = month - dp2.month;
  30400. if (result == 0)
  30401. result = day - dp2.day;
  30402. return result;
  30403. }
  30404. // End of File
  30405.  
  30406.  
  30407. Listing 9 Illustrates the Date Class
  30408.  
  30409. //tdate3.cpp
  30410. #include <stdio.h>
  30411. #include "date3.h"
  30412.  
  30413. #define DATELEN 19
  30414.  
  30415. main()
  30416. {
  30417. Date d1(10,1,1951), d2(3,7,1995);
  30418. char buf[DATELEN+1];
  30419. int cmp;
  30420.  
  30421. printf("d1 == %s\n",d1.format(buf)