Advertisement
spikeysnack

interface.cpp

Jan 9th, 2018
776
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.17 KB | None | 0 0
  1. /* interface.cpp */
  2.  
  3. #include <memory>
  4. #include <iostream>
  5. #include <iomanip>
  6. #include <vector>
  7. #include <cmath>
  8.  
  9.  
  10. /**
  11.     @Author Chris Reid <[email protected]>
  12.     @Date   23 May 2017
  13.     @License freeware
  14.     @Purpose demonstration
  15. */
  16.  
  17.  
  18. /* Demonstration of the C++ interface model.
  19.  
  20.    Similar in spirit to Java's interface class,
  21.    and Objective-C's Protocols as well,
  22.    C++ allows classes to define interfaces (member functions)
  23.    with no actual code in them. They are declared as
  24.    'pure virtual' functions, and the compiler is hinted
  25.    as to exactly what they are by assignment to 0.
  26.  
  27.    The compiler then enforces that any classes that
  28.    inherit from the interface class must implement
  29.    the member function. Simple. Sort of.
  30.    
  31.    There is some added confusion in that:
  32.    a virtual function may or may not have a body of code in a base class.
  33.  
  34.    Virtual means only that late binding of member functions
  35.    should take place, so that the subclass function is bound
  36.    to the object regardless if the object is treated as a
  37.   parent class.
  38.    
  39. */
  40.  
  41. // rotation angle
  42. #define R_ANGLE  45.0f
  43.  
  44. // some numeric constants and lambdas
  45. static float Tau = 6.28318530718f;          // Circle constant
  46. static float pi = Tau / 2.0f;               // legacy constant
  47. static float deg_to_rad = Tau/360.0f;       // conversion constant
  48. static float rad_to_deg = 1.0f/deg_to_rad;  // conversion constant
  49.  
  50. // lambda function convert to radians
  51. auto radians = [&] (float f) -> float
  52. { return f * deg_to_rad; };  
  53.  
  54. // lambda function convert to degrees
  55. auto degrees = [&] (float f) -> float
  56. { return f * rad_to_deg; };  
  57.  
  58.   // lambda round to 5 places
  59. auto fround = [&](float f, size_t n) -> float
  60. {
  61.   float _p = std::pow(10,n);
  62.   return std::round(f * _p) / _p;
  63. };  
  64.  
  65.  
  66.  
  67. /* definition of classes
  68.    Rotatable:  interface class
  69.    Shape    :  interface class
  70.    Point    :  data struct
  71.    Line     :  concrete subclass of Shape and Rotatable
  72. */
  73.  
  74.  
  75. // An interface class
  76. class Rotatable
  77. {
  78. public:
  79.   virtual void rotate ( float angle ) = 0; // "pure virtual" method
  80. };
  81.  
  82.  
  83. // An interface class
  84. class Shape  
  85. {
  86. public:
  87.   //  virtual ~Shape(){}; //
  88.   virtual void move_x(float x) = 0; // pure virtual method
  89.   virtual void move_y(float y) = 0;
  90.   virtual void draw() = 0;
  91.   //...
  92. };
  93.  
  94.  
  95.  
  96. // a data struct
  97. struct Point
  98. {  
  99.   Point( float _x , float _y ) : x(_x) , y(_y) {}
  100.  
  101.   Point(const Point& p) : x(p.x) , y(p.y) {}
  102.  
  103.   //  ~Point() {}
  104.  
  105.   Point& operator=(const Point& p)
  106.   {
  107.     x = p.x;
  108.     y = p.y;
  109.     return *this;
  110.   }
  111.  
  112.   float x;
  113.   float y;
  114. };
  115.  
  116.  
  117.  
  118.  
  119. // An concrete class that implements Shape and Rotatable
  120. // interface
  121. class Line : public Shape , public Rotatable
  122. {
  123. public:
  124.  
  125.   Line( const Point& pa , const Point& pb);
  126.   ~Line();
  127.   void move_x(float _x);         // implements move_x
  128.   void move_y(float _y);         // implements move_y
  129.  
  130.   void draw();                  // implements draw
  131.   void rotate ( float angle); // concrete
  132.  
  133. private:
  134.   Point end_point_1;
  135.   Point end_point_2;
  136.  
  137. };
  138.  
  139. // implementation
  140.  
  141. //constructor
  142. Line::Line( const Point& pa , const Point& pb) :end_point_1(pa) , end_point_2(pb) {}
  143.  
  144. //destructor
  145. Line::~Line(){}
  146.  
  147. //from Shape
  148. void Line::move_x(float _x){ end_point_1.x += _x; end_point_2.x += _x; } // implements move_x
  149.  
  150. void Line::move_y(float _y){ end_point_1.y += _y; end_point_2.y += _y; } // implements move_x
  151.  
  152. void Line::draw()
  153. {
  154.  
  155.   float X(end_point_2.x - end_point_1.x);
  156.   float Y(end_point_2.y - end_point_1.y);
  157.    
  158.   float slope = Y/X;
  159.    
  160.   //  if (slope < 0 ) slope = std::fmod ( (Tau - slope) , Tau);
  161.    
  162.   //  if (X < 0 ) slope = (Tau - slope);
  163.    
  164.   float m = std::atan2( Y, X );
  165.    
  166.   // set output format
  167.   std::cout << std::setprecision(5);  
  168.    
  169.   std::cout << "Line:"
  170.         << "\t("  << end_point_1.x << ", " <<  end_point_1.y  << ")"
  171.         << " , ("  << end_point_2.x << ", " <<  end_point_2.y  << ")"
  172.         << "\tm: " << m << " radians " <<  degrees(m) << " degrees";
  173.    
  174.   std::cout << std::endl;
  175.    
  176. }
  177.  
  178.  
  179.  
  180. // from Rotatable
  181. void Line::rotate( float angle) {
  182.  
  183.   float angle_rad = radians(angle);
  184.  
  185.   std::cout << "rotate\t"
  186.         << angle << " degrees = "
  187.         << angle_rad << " radians"
  188.         << std::endl;
  189.  
  190.   float x1(end_point_1.x);
  191.   float x2(end_point_2.x);
  192.   float y1(end_point_1.y);
  193.   float y2(end_point_2.y);
  194.  
  195.   Point midpoint = { ((x1 + x2 )/2.0f) , ((y1 + y2 )/2.0f) };
  196.   //  float distance = std::sqrt( (x2 -x1) * (x2 -x1)  + (y2 -y1) * (y2 -y1)  );
  197.   //  float halflength =  distance /2.0f;
  198.  
  199.   std::cout << "midpoint:\t ("
  200.         << midpoint.x << " , "
  201.         << midpoint.y << ")"
  202.         << std::endl;  
  203.  
  204.   float X =  ( x2 - x1 );
  205.   float Y =  ( y2 - y1 );
  206.   float slope =  Y/X;
  207.   float current_angle_rad = std::atan2(Y,X); // current angle
  208.  
  209.   //  float theta = current_angle_rad + angle_rad;  // convert to radians
  210.  
  211.   float theta = angle_rad;  // convert to radians
  212.  
  213.   std::cout << "theta = " << theta << " radians "
  214.         << degrees(theta) << " degrees" << std::endl;
  215.  
  216.  
  217.   //rotate from origin
  218.   // float X1 = x1* std::cos(theta) - y1 * std::sin(theta);
  219.   // float Y1 = x1* std::sin(theta) + y1 * std::cos(theta);
  220.   // float X2 = x2* std::cos(theta) -  y2 * std::sin(theta);
  221.   // float Y2 = x2* std::sin(theta) +  y2 * std::cos(theta);
  222.  
  223.   // normalize and rotate about midpoint
  224.   float X1 = (std::cos(theta) * (x1 - midpoint.x) ) - ( std::sin(theta) * ( y1 - midpoint.y) );
  225.   float Y1 = (std::sin(theta) * (x1 - midpoint.x) ) + ( std::cos(theta) * ( y1 - midpoint.y) );
  226.   float X2 = (std::cos(theta) * (x2 - midpoint.x) ) - ( std::sin(theta) * ( y2 - midpoint.y) );
  227.   float Y2 = (std::sin(theta) * (x2 - midpoint.x) ) + ( std::cos(theta) * ( y2 - midpoint.y) );
  228.  
  229.   // add midpoint offset back in
  230.   X1 += midpoint.x;
  231.   X2 += midpoint.x;
  232.   Y1 += midpoint.y;
  233.   Y2 += midpoint.y;
  234.  
  235.  
  236.   X1 =  fround(X1, 5);
  237.   Y1 =  fround(Y1, 5);
  238.   X2 =  fround(X2, 5);
  239.   Y2 =  fround(Y2, 5);
  240.  
  241.  
  242.   // update end_points
  243.   end_point_1.x =  X1;
  244.   end_point_1.y =  Y1;
  245.   end_point_2.x =  X2;
  246.   end_point_2.y =  Y2;
  247.  
  248. }
  249.  
  250.  
  251.  
  252.  
  253. /**************************************************/
  254.  
  255.  
  256. // some runtime type info (rtti) functions
  257.  
  258. // is it a Rotatable class?
  259. template <class T>
  260. inline bool is_Rotatable( T& r)
  261. {
  262.   // dynamic_cast returns nullptr on fail to cast
  263.   // nullptr (false, 0)  means "not a Rotatable"
  264.   return ( dynamic_cast<Rotatable*>(r) )? true : false;
  265. }
  266.  
  267.  
  268.  
  269. // wrapper function
  270. // calls class function
  271. template <class T>
  272. void rotate( T& r, float angle)
  273. {
  274.   // check first then try casting
  275.   if ( is_Rotatable(r) )
  276.     dynamic_cast<Rotatable*>(r)->rotate(angle);
  277. }
  278.  
  279.  
  280. int test1 (void);
  281. int test2 (void);
  282. int test3 (void);
  283.  
  284.  
  285. int test1 (void)
  286. {
  287.   // set output format
  288.   std::cout << std::setprecision(5);  
  289.  
  290.   std::vector<Shape*> Shapes;
  291.  
  292.   Point a = { 0.0 ,  0.0 }; // initialization list
  293.   Point b = { 0.0 , 10.0 };
  294.  
  295.   Line* l1 = new Line(a,b);      // dynamic allocation
  296.   l1->draw();
  297.  
  298.   Line* l2 = new Line(b,a);      // dynamic allocation  Line l2(b,a);  // dynamic allocation
  299.   l2->draw();
  300.  
  301.   Shapes.push_back( l1 );       // add to vector
  302.   Shapes.push_back( l2 );       // add to vector
  303.  
  304.   //  Shapes.back()->draw();
  305.    
  306.   for ( auto l : Shapes)      // returns i as (Shape*)
  307.     {
  308.       rotate(l, R_ANGLE  );       // safe rotate
  309.       l->draw();              // calls Line.draw()
  310.     }
  311.  
  312.  
  313.   /* clean up  
  314.      --note:
  315.            std::vector does not call its content's destructor for pointers
  316.            as it does for objects. (hmmm).
  317.   */
  318.  
  319.   for ( auto l : Shapes)   delete l;    // l is seen as a (Shape*)
  320.  
  321.  
  322.   return 0;
  323. }
  324.  
  325.  
  326.  
  327. /* std::unique_ptr<Shape> can be used to
  328.   clean up items automatically even if they
  329.   are by necessity added to containers by a
  330.   pointer to an interface class.
  331. */
  332.  
  333. // unique pointer macro  (we are using Shape)
  334. #define US_PTR std::unique_ptr<Shape>
  335.  
  336. int test2 (void)
  337. {
  338.   // set output format
  339.   std::cout << std::setprecision(5);  
  340.  
  341.   Point a = { 0.0 ,  0.0 }; // initialization list
  342.   Point b = { 0.0 , 10.0 };
  343.  
  344.  
  345.   US_PTR l1(new Line(a,b));       // dynamic allocation
  346.   l1->draw();
  347.  
  348.   US_PTR l2(new Line(b,a));       // dynamic allocation      
  349.   l2->draw();
  350.  
  351.  
  352.   //  Shape pointers in a vector
  353.  
  354.   std::vector<Shape*> Shapes;
  355.   Shapes.push_back( l1.get() );         // add contents of unique_ptr to vector
  356.   Shapes.push_back( l2.get() );         // unique_ptr.get() -> (Line*)
  357.  
  358.   //  Shapes.back()->draw();
  359.    
  360.   for ( auto l : Shapes)      // returns i as (Shape*)
  361.     {
  362.       rotate(l, R_ANGLE );       // safe rotate      
  363.       l->draw();              // calls Line.draw()
  364.     }
  365.  
  366.   /* now when l1 and l2 fo out of scope, they call
  367.      the destructors for the line objects that they point to. */
  368.  
  369.   return 0;
  370. }
  371.  
  372.  
  373. /* std::vector does call the destructors of objects
  374.    it can. However, trying to add a non-copyable class
  375.    like shape ( no constructor or copy constructor)
  376.    will mess up std:vector. So here we add them by
  377.    address */
  378. int test3 (void)
  379. {
  380.  
  381.   Shape* sp; // point to a shape object
  382.  
  383.   // set output format
  384.   std::cout << std::setprecision(5);  
  385.  
  386.   Point a = { 0.0 ,  0.0 }; // initialization list
  387.   Point b = { 0.0 , 10.0 };
  388.  
  389.   Line l1(a,b);      
  390.  
  391.   l1.draw();
  392.  
  393.   Line l2(b,a);      
  394.   l2.draw();
  395.  
  396.   //  std::vector<Shape> Shapes; // ERROR! Shape is an abstract class!
  397.  
  398.   std::vector<Shape*> Shapes;    // OK
  399.  
  400. // Shapes.push_back( l1 ) ;    // ERROR! std::vector won't allow a Line in place of a Shape
  401.  
  402.  
  403.   std::cerr <<  "TYPE &l1:\t" << typeid(&l1).name() << std::endl;
  404.  
  405.  
  406.   sp = std::addressof(l1);         // works because a Line ISA Shape
  407.  
  408.   Shapes.push_back( sp );         // add contents by quick pointer
  409.  
  410.  
  411.   sp = std::addressof(l2);
  412.   Shapes.push_back( sp );         // (Shape*)
  413.  
  414.   sp = NULL;                      // no dangling pointers
  415.  
  416.   //  Shapes.back()->draw();
  417.    
  418.   for ( auto* s : Shapes)     // returns i as (Shape*)
  419.     {
  420.       rotate(s, R_ANGLE );    // safe rotate      
  421.       s->draw();              // calls (Line*)->draw()
  422.     }
  423.  
  424.  
  425.  
  426.   return 0;
  427. }
  428.  
  429.  
  430. int main()
  431. {
  432.   int r = test3();
  433.   return r;
  434. }
  435.  
  436.  
  437.  
  438.  
  439. /*END*/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement