home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1988 / 09 / grosberg.lis next >
File List  |  1988-08-22  |  12KB  |  524 lines

  1. _OBJECT-ORIENTED DIMENSIONAL UNITS_
  2. by
  3. John A. Grosbery
  4.  
  5.  
  6.  
  7. Listing One
  8.  
  9. package float_unit is
  10. type class is new float;
  11. units_error : exception;
  12.  
  13. function "*" (left,right : class) return class; 
  14.     -- This function is to overload the inherited 
  15.     -- multiply function. Multiplying two dimensioned 
  16.     -- numbers does not produce a number with the same 
  17.     -- units, so this is an invalid operation.  It will
  18.     -- raise the units_error exception. 
  19.  
  20. -- The following multiplication functions provide for
  21. -- multiplying a non-dimensional number (float or
  22. -- integer) times a dimensional number (class). There
  23. -- are two of each (one with float first, one with
  24. -- class first) to make the multiplication functions
  25. -- commutative.
  26.  
  27. function "*" (left : float; right : class) return class;
  28. function "*" (left : class; right : float) return class;
  29.  
  30. function "*" (left : integer; right : class)   return 
  31. class;
  32. function "*" (left : class;   right : integer) return 
  33. class;
  34.  
  35. function "/" (left,right : class) return class;
  36.     -- This function is to overload the inherited 
  37.     -- divide function. Dividing two dimensioned numbers 
  38.     -- does not produce a number with the same units, so 
  39.     -- this is an invalid operation.  It will raise the
  40.     -- units_error exception. 
  41.  
  42. function "/" (left, right : class) return float;
  43.     -- This function divides two items of type class and
  44.     -- returns the result as type float.  Dividing a 
  45.     -- dimensioned number by another of the same 
  46.     -- dimensioned produces a non-dimensional number.
  47.  
  48. -- The next two divide functions allow dividing a
  49. -- dimensioned number by a non-dimensioned floating point
  50. -- or integer number.  Doing so produces a result with
  51. -- the same dimensions as the dimensioned number. 
  52.  
  53. function "/" (left : class; right : float) return class;
  54. function "/" (left : class; right : integer) return 
  55. class;
  56.  
  57. function "**" (left:class; right:integer) return class;
  58.     -- This function is to overload the inherited 
  59.     -- exponentiation function. Exponentiating 
  60.     -- dimensioned numbers does not produce a 
  61.     -- number with the same units, so this is an 
  62.     -- invalid operation.  It will raise the
  63.     -- units_error exception. 
  64.  
  65. function image ( the_object :in class ) return string;
  66.     -- This function will take the_object of type 
  67.     -- class and convert it to a string type.  The 
  68.     -- name "image" was chosen because the purpose of 
  69.     -- this function is similar to that of Ada's "image"     
  70.         -- attribute.  This function and the following
  71.     -- decouple the units package from any input/output 
  72.     -- device or package.
  73.  
  74.  
  75. function value (the_string :in string) return class;
  76.     -- This function will take a string which is a valid 
  77.     -- representation of an object of the type class and 
  78.     -- convert it to the type class.  If the_string 
  79.     -- contains an invalid value, the constraint_error
  80.     -- exception will be raised.  The name "value" was 
  81.     -- used because the purpose of this function is 
  82.     -- similar to Ada's "value" attribute.
  83.  
  84. end float_unit;
  85.  
  86. with text_io;
  87.  
  88. package body float_unit is
  89. ------------------------------------------------------------
  90. function "*" (left,right : class) return class is
  91.     -- This function is to hide the inherited multiply 
  92.     -- function. Multiplying two dimensioned numbers does 
  93.     -- not produce a number with the same units, so 
  94.     -- this is an invalid operation.  If this function 
  95.     -- is invoked, it will raise the units_error exception. 
  96.  
  97. begin
  98.     -- Whole function invalid; force exception:
  99.  
  100.     raise units_error;    
  101.     return left * right; 
  102.  
  103.     -- Above return needed to satisfy compiler, but 
  104.     -- it will never be executed.
  105. end "*";
  106.  
  107. function "*" (left : float; right : class) return class
  108. is
  109. begin
  110.     return class(left * float(right));
  111. end "*";
  112.  
  113.  
  114. function "*" (left : class; right : float) return class
  115. is
  116. begin
  117.     return class( float(left) * right );
  118. end "*";
  119.  
  120. function "*" (left : integer; right : class)   return 
  121. class
  122. is
  123. begin
  124.     return class( float(left) * right );
  125. end "*";
  126.  
  127. function "*" (left : class;   right : integer) return 
  128. class
  129. is
  130. begin
  131.     return class( left * float(right) );
  132. end "*";
  133.  
  134. function "/" (left,right : class) return class
  135. is
  136. begin
  137.     -- Whole function invalid; force exception:
  138.  
  139.     raise units_error;  
  140.     return class( float(left) / float(right)); 
  141.  
  142.     -- Above return needed to satisfy compiler, but 
  143.     -- it will never be executed.
  144. end "/";
  145.  
  146. function "/" (left, right : class) return float
  147. is
  148. begin
  149.     return float(left) / float(right);
  150. end "/";
  151.  
  152. function "/" (left : class; right : float) return class
  153. is
  154. begin
  155.     return class( float(left) / right);
  156. end "/";
  157.  
  158. function "/" (left : class; right : integer) return class
  159. is
  160. begin
  161.     return class( float(left) / float(right) );
  162. end "/";
  163.  
  164. function "**" (left:class; right:integer) return class
  165. is
  166. begin
  167.     raise units_error;
  168.     return class( float(left) ** right);
  169. end "**";
  170.  
  171. package fio is new text_io.float_io(class); 
  172. -- Fio will be needed by image and value, below.
  173.  
  174. function image ( the_object :in class ) return string
  175. is
  176.     buffer : string(1..14);
  177. begin
  178.     fio.put(buffer, the_object);
  179.     return buffer;
  180. end image;    
  181.  
  182. function value (the_string :in string) return class
  183. is
  184.     buffer : class;
  185.     last   : positive;
  186. begin
  187.     fio.get(the_string, buffer, last); 
  188.     return buffer;
  189. end value;
  190.  
  191. end float_unit;
  192.  
  193.  
  194.  
  195.  
  196. Listing Two
  197.  
  198. ------------------------------------------------------------
  199. with float_unit;
  200.  
  201. generic 
  202.     type class_a is digits<>;
  203.     type class_b is digits <>;
  204. package product_unit is
  205.  
  206. type class is new float_unit.class;
  207.  
  208. function "*"(left    : class_a;
  209.             right    : class_b) return class;
  210.  
  211. function "*"(left    : class_b;
  212.             right    : class_a) return class;
  213.  
  214. function "/"(left    : class; 
  215.             right    : class_a) return class_b;
  216.  
  217. function "/"(left    : class; 
  218.             right    : class_b) return class_a;
  219.  
  220. end product_unit;
  221.  
  222. package body product_unit is
  223.     
  224. function "*"(left    : class_a;
  225.             right    : class_b) return class
  226. is
  227. begin
  228.     return class(float(left) * float(right));
  229. end "*";
  230.  
  231. function "*"(left    : class_b;
  232.             right    : class_a) return class
  233. is
  234. begin
  235.     return class(float(left) * float(right));
  236. end "*";
  237.  
  238. function "/"(left    : class; 
  239.             right    : class_a) return class_b
  240. is
  241. begin
  242.     return class_b(float(left) / float(right));
  243. end "/";
  244.  
  245. function "/"(left    : class; 
  246.             right    : class_b) return class_a
  247. is
  248. begin
  249.     return class_a(float(left) / float(right));
  250. end "/";
  251.         
  252. end product_unit;
  253.  
  254.  
  255.  
  256. Listing Three
  257. ------------------------------------------------------------
  258.  
  259. with float_unit;
  260.  
  261. generic
  262.     type numerator_class is digits <>;
  263.     type denominator_class is digits <>;
  264. package quotient_unit is
  265.  
  266. type class is new float_unit.class;
  267.  
  268. function "/"(left    : numerator_class;
  269.                  right    : denominator_class
  270. ) return class;
  271.  
  272. function "*"(left    : class;
  273.             right    : denominator_class
  274. ) return numerator_class;
  275.  
  276. function "*"(left    : denominator_class;
  277.             right    : class
  278. ) return numerator_class;        
  279.  
  280. end quotient_unit;
  281.  
  282. package body quotient_unit is
  283.  
  284. function "/"(left    : numerator_class;
  285.             right    : denominator_class) return class
  286. is
  287. begin
  288.     return class(float(left) / float(right));
  289. end "/";
  290.  
  291. function "*"(left    : class;
  292.             right    : denominator_class
  293. ) return numerator_class
  294. is
  295. begin
  296.     return numerator_class(float(left) * float(right));
  297. end "*";
  298.  
  299. function "*"(left    : denominator_class;
  300.             right    : class
  301. ) return numerator_class
  302. is
  303. begin
  304.     return numerator_class(float(left) * float(right));
  305. end "*";
  306.  
  307. end quotient_unit;
  308.  
  309.  
  310.  
  311. Example 1: Using the form for a package construct
  312.  
  313.  
  314.         package float_unit  is
  315.             type class is new float; 
  316.     
  317.             function"*"(left : float;    
  318.                     right: class    
  319.                 ) return  class;
  320.  
  321.             function "/" (left: class;
  322.                     right: float    
  323.                 ) return class;
  324.  
  325.             -- etc... 
  326.         end float_unit;
  327.  
  328.  
  329.  
  330. Example 2: Creating objects of the hour glass
  331.  
  332.         with hour;  use hour;
  333.         procedure time_card is
  334.             -- Create the objects:
  335.             hours_worked : hour.class;
  336.             job_1 : hour.class;
  337.             job_2 : hour.class;
  338.  
  339.         begin
  340.             -- Give them each a value:
  341.             job_1 := 8.0;
  342.             job_2 := 5.5;
  343.             hours_worked := job_1 + job_2;
  344.  
  345.         end time_card;
  346.  
  347.  
  348.  
  349. Example 3: Using the hour class and a new mile class to create 
  350. the mile_per_hour class
  351.  
  352.         with float_unit;
  353.         package mile is new unit;
  354.         
  355.         with float_unit;
  356.         with hour;
  357.         with mile;
  358.  
  359.         package mile_per_hour is
  360.             type class is new float_unit.class;
  361.  
  362.             function "/"(left : mile.class;  
  363.                           right: hour.class
  364.                         ) return class;
  365.  
  366.         end mile_per_hour;
  367.  
  368.  
  369. Example 4: Installing the specification and the body for the 
  370. packages listed in Example 2 and 3
  371.  
  372.         with hour;
  373.         with mile;
  374.         with quotient_unit;
  375.  
  376.         package mile_per_hour is new quotient_unit(
  377.             numerator_class => mile.class,
  378.             denominator_class => hour.class);
  379.  
  380.  
  381. Example 5: Creating new composite units by applying an existing 
  382. generic package as many times as necessary. In this case, a 
  383. package for cubic feet is created from miles/hour.
  384.  
  385.         with unit;  
  386.         package foot is new unit;
  387.  
  388.         with foot;  
  389.         with product_unit;
  390.         package square_foot is new product_unit(
  391.             class_a => foot, 
  392.             class_b => foot);
  393.  
  394.  
  395.         with foot;
  396.         with square_foot;
  397.         with product_unit;
  398.         package cubic_foot is new product_unit(
  399.             class_a => foot,
  400.             class_b => square_foot);
  401.  
  402.  
  403.  
  404. Example 6: Converting routines to couple a package with other 
  405. packages
  406.  
  407.         with float_unit;
  408.         with hour;
  409.         with mile;
  410.         with mile_per_second;
  411.  
  412.         package mile_per_hour is
  413.             type class is new float_unit.class;
  414.  
  415.             function "/"(left  : mile.class;  
  416.                            right : hour.class
  417.                     ) return class;
  418.  
  419.  
  420.             function convert (mps :
  421.                     miles_per_second.class
  422.                     ) return class;
  423.         end mile_per_hour;
  424.  
  425.  
  426.  
  427. Example 7: Modelling relationships on objects
  428.  
  429.  
  430.         with mile_per_hour;
  431.         with mile_per_second;
  432.         package mph_mps_convert  is
  433.  
  434.             function relation(mph : 
  435.                         mile_per_hour.class)
  436.                 return mile_per_second.class;
  437.  
  438.             function relation(mps :
  439.                         mile_per_second.class)
  440.                 return mile_per_hour.class;
  441.         end mph_mps_convert;
  442.  
  443.  
  444. Example 8: Generalizing relationship objects for dimensional unit 
  445. applications by creating a class that provides functions to go 
  446. both ways. This generalization process is implemented as a 
  447. generic package that imports the conversion factor and the two 
  448. objects that are to be related.
  449.  
  450.  
  451.         generic
  452.             -- Import one kind of class:
  453.             type class_a is digits <>;
  454.             -- Import the other kind:
  455.             type class_b is digits <>;             
  456.                          -- Import the conversion factor
  457.             a_to_b_factor : in float := 1.0;
  458.         package class_a_class_b_convert is
  459.  
  460.             function relation (a : class_a
  461.                     ) return class_b;
  462.  
  463.  
  464.             function relation (b : class_b
  465.                     ) return class_a;
  466.  
  467.         end class_a_class_b_convert;
  468.  
  469.  
  470.         package body class_a_class_b_convert is
  471.  
  472.             function relation (a : class_a
  473.                     ) return class_b 
  474.             is
  475.             begin
  476.                 return class_b(float(a) * 
  477.                             a_to_b_factor);
  478.             end relation;
  479.  
  480.  
  481.             function relation (b : class_b
  482.                     ) return class_a;
  483.             is
  484.             begin
  485.                 return class_a(float(b) /
  486.                          a_to_b_factor);
  487.             end relation;
  488.  
  489.         end class_a_class_b_convert;
  490.  
  491.  
  492.  
  493. Example 9: Using the relationship objects described in Example 8
  494.  
  495.  
  496.         with miles_per_hour;
  497.         with miles_per_second;
  498.         with class_a_class_b_convert;
  499.         package mph_mps_convert is 
  500.                 new class_a_class_b_convert( 
  501.             class_a => miles_per_hour.class,
  502.             class_b => miles_per_second.class,
  503.             a_to_b_factor => 3600.0);
  504.  
  505.  
  506. Example 10: Code fragment showing the use of the mph_mps.convert 
  507. object created in Example 9 to convert 60 mile_per_hour into 
  508. mile_per_second
  509.  
  510.  
  511.         with miles_per_hour;    use miles_per_hour;
  512.         with miles_per_second;    use miles_per_second;
  513.         with mph_mps_convert;
  514.         ...
  515.         mph : miles_per_hour.class := 60.0; 
  516.         mps  : miles_per_second.class;
  517.         ...
  518.         mps  := mph_mps_convert.relation(mph); 
  519.         ...
  520.  
  521.  
  522.  
  523.  
  524.