Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* interface.cpp */
- #include <memory>
- #include <iostream>
- #include <iomanip>
- #include <vector>
- #include <cmath>
- /**
- @Author Chris Reid <[email protected]>
- @Date 23 May 2017
- @License freeware
- @Purpose demonstration
- */
- /* Demonstration of the C++ interface model.
- Similar in spirit to Java's interface class,
- and Objective-C's Protocols as well,
- C++ allows classes to define interfaces (member functions)
- with no actual code in them. They are declared as
- 'pure virtual' functions, and the compiler is hinted
- as to exactly what they are by assignment to 0.
- The compiler then enforces that any classes that
- inherit from the interface class must implement
- the member function. Simple. Sort of.
- There is some added confusion in that:
- a virtual function may or may not have a body of code in a base class.
- Virtual means only that late binding of member functions
- should take place, so that the subclass function is bound
- to the object regardless if the object is treated as a
- parent class.
- */
- // rotation angle
- #define R_ANGLE 45.0f
- // some numeric constants and lambdas
- static float Tau = 6.28318530718f; // Circle constant
- static float pi = Tau / 2.0f; // legacy constant
- static float deg_to_rad = Tau/360.0f; // conversion constant
- static float rad_to_deg = 1.0f/deg_to_rad; // conversion constant
- // lambda function convert to radians
- auto radians = [&] (float f) -> float
- { return f * deg_to_rad; };
- // lambda function convert to degrees
- auto degrees = [&] (float f) -> float
- { return f * rad_to_deg; };
- // lambda round to 5 places
- auto fround = [&](float f, size_t n) -> float
- {
- float _p = std::pow(10,n);
- return std::round(f * _p) / _p;
- };
- /* definition of classes
- Rotatable: interface class
- Shape : interface class
- Point : data struct
- Line : concrete subclass of Shape and Rotatable
- */
- // An interface class
- class Rotatable
- {
- public:
- virtual void rotate ( float angle ) = 0; // "pure virtual" method
- };
- // An interface class
- class Shape
- {
- public:
- // virtual ~Shape(){}; //
- virtual void move_x(float x) = 0; // pure virtual method
- virtual void move_y(float y) = 0;
- virtual void draw() = 0;
- //...
- };
- // a data struct
- struct Point
- {
- Point( float _x , float _y ) : x(_x) , y(_y) {}
- Point(const Point& p) : x(p.x) , y(p.y) {}
- // ~Point() {}
- Point& operator=(const Point& p)
- {
- x = p.x;
- y = p.y;
- return *this;
- }
- float x;
- float y;
- };
- // An concrete class that implements Shape and Rotatable
- // interface
- class Line : public Shape , public Rotatable
- {
- public:
- Line( const Point& pa , const Point& pb);
- ~Line();
- void move_x(float _x); // implements move_x
- void move_y(float _y); // implements move_y
- void draw(); // implements draw
- void rotate ( float angle); // concrete
- private:
- Point end_point_1;
- Point end_point_2;
- };
- // implementation
- //constructor
- Line::Line( const Point& pa , const Point& pb) :end_point_1(pa) , end_point_2(pb) {}
- //destructor
- Line::~Line(){}
- //from Shape
- void Line::move_x(float _x){ end_point_1.x += _x; end_point_2.x += _x; } // implements move_x
- void Line::move_y(float _y){ end_point_1.y += _y; end_point_2.y += _y; } // implements move_x
- void Line::draw()
- {
- float X(end_point_2.x - end_point_1.x);
- float Y(end_point_2.y - end_point_1.y);
- float slope = Y/X;
- // if (slope < 0 ) slope = std::fmod ( (Tau - slope) , Tau);
- // if (X < 0 ) slope = (Tau - slope);
- float m = std::atan2( Y, X );
- // set output format
- std::cout << std::setprecision(5);
- std::cout << "Line:"
- << "\t(" << end_point_1.x << ", " << end_point_1.y << ")"
- << " , (" << end_point_2.x << ", " << end_point_2.y << ")"
- << "\tm: " << m << " radians " << degrees(m) << " degrees";
- std::cout << std::endl;
- }
- // from Rotatable
- void Line::rotate( float angle) {
- float angle_rad = radians(angle);
- std::cout << "rotate\t"
- << angle << " degrees = "
- << angle_rad << " radians"
- << std::endl;
- float x1(end_point_1.x);
- float x2(end_point_2.x);
- float y1(end_point_1.y);
- float y2(end_point_2.y);
- Point midpoint = { ((x1 + x2 )/2.0f) , ((y1 + y2 )/2.0f) };
- // float distance = std::sqrt( (x2 -x1) * (x2 -x1) + (y2 -y1) * (y2 -y1) );
- // float halflength = distance /2.0f;
- std::cout << "midpoint:\t ("
- << midpoint.x << " , "
- << midpoint.y << ")"
- << std::endl;
- float X = ( x2 - x1 );
- float Y = ( y2 - y1 );
- float slope = Y/X;
- float current_angle_rad = std::atan2(Y,X); // current angle
- // float theta = current_angle_rad + angle_rad; // convert to radians
- float theta = angle_rad; // convert to radians
- std::cout << "theta = " << theta << " radians "
- << degrees(theta) << " degrees" << std::endl;
- //rotate from origin
- // float X1 = x1* std::cos(theta) - y1 * std::sin(theta);
- // float Y1 = x1* std::sin(theta) + y1 * std::cos(theta);
- // float X2 = x2* std::cos(theta) - y2 * std::sin(theta);
- // float Y2 = x2* std::sin(theta) + y2 * std::cos(theta);
- // normalize and rotate about midpoint
- float X1 = (std::cos(theta) * (x1 - midpoint.x) ) - ( std::sin(theta) * ( y1 - midpoint.y) );
- float Y1 = (std::sin(theta) * (x1 - midpoint.x) ) + ( std::cos(theta) * ( y1 - midpoint.y) );
- float X2 = (std::cos(theta) * (x2 - midpoint.x) ) - ( std::sin(theta) * ( y2 - midpoint.y) );
- float Y2 = (std::sin(theta) * (x2 - midpoint.x) ) + ( std::cos(theta) * ( y2 - midpoint.y) );
- // add midpoint offset back in
- X1 += midpoint.x;
- X2 += midpoint.x;
- Y1 += midpoint.y;
- Y2 += midpoint.y;
- X1 = fround(X1, 5);
- Y1 = fround(Y1, 5);
- X2 = fround(X2, 5);
- Y2 = fround(Y2, 5);
- // update end_points
- end_point_1.x = X1;
- end_point_1.y = Y1;
- end_point_2.x = X2;
- end_point_2.y = Y2;
- }
- /**************************************************/
- // some runtime type info (rtti) functions
- // is it a Rotatable class?
- template <class T>
- inline bool is_Rotatable( T& r)
- {
- // dynamic_cast returns nullptr on fail to cast
- // nullptr (false, 0) means "not a Rotatable"
- return ( dynamic_cast<Rotatable*>(r) )? true : false;
- }
- // wrapper function
- // calls class function
- template <class T>
- void rotate( T& r, float angle)
- {
- // check first then try casting
- if ( is_Rotatable(r) )
- dynamic_cast<Rotatable*>(r)->rotate(angle);
- }
- int test1 (void);
- int test2 (void);
- int test3 (void);
- int test1 (void)
- {
- // set output format
- std::cout << std::setprecision(5);
- std::vector<Shape*> Shapes;
- Point a = { 0.0 , 0.0 }; // initialization list
- Point b = { 0.0 , 10.0 };
- Line* l1 = new Line(a,b); // dynamic allocation
- l1->draw();
- Line* l2 = new Line(b,a); // dynamic allocation Line l2(b,a); // dynamic allocation
- l2->draw();
- Shapes.push_back( l1 ); // add to vector
- Shapes.push_back( l2 ); // add to vector
- // Shapes.back()->draw();
- for ( auto l : Shapes) // returns i as (Shape*)
- {
- rotate(l, R_ANGLE ); // safe rotate
- l->draw(); // calls Line.draw()
- }
- /* clean up
- --note:
- std::vector does not call its content's destructor for pointers
- as it does for objects. (hmmm).
- */
- for ( auto l : Shapes) delete l; // l is seen as a (Shape*)
- return 0;
- }
- /* std::unique_ptr<Shape> can be used to
- clean up items automatically even if they
- are by necessity added to containers by a
- pointer to an interface class.
- */
- // unique pointer macro (we are using Shape)
- #define US_PTR std::unique_ptr<Shape>
- int test2 (void)
- {
- // set output format
- std::cout << std::setprecision(5);
- Point a = { 0.0 , 0.0 }; // initialization list
- Point b = { 0.0 , 10.0 };
- US_PTR l1(new Line(a,b)); // dynamic allocation
- l1->draw();
- US_PTR l2(new Line(b,a)); // dynamic allocation
- l2->draw();
- // Shape pointers in a vector
- std::vector<Shape*> Shapes;
- Shapes.push_back( l1.get() ); // add contents of unique_ptr to vector
- Shapes.push_back( l2.get() ); // unique_ptr.get() -> (Line*)
- // Shapes.back()->draw();
- for ( auto l : Shapes) // returns i as (Shape*)
- {
- rotate(l, R_ANGLE ); // safe rotate
- l->draw(); // calls Line.draw()
- }
- /* now when l1 and l2 fo out of scope, they call
- the destructors for the line objects that they point to. */
- return 0;
- }
- /* std::vector does call the destructors of objects
- it can. However, trying to add a non-copyable class
- like shape ( no constructor or copy constructor)
- will mess up std:vector. So here we add them by
- address */
- int test3 (void)
- {
- Shape* sp; // point to a shape object
- // set output format
- std::cout << std::setprecision(5);
- Point a = { 0.0 , 0.0 }; // initialization list
- Point b = { 0.0 , 10.0 };
- Line l1(a,b);
- l1.draw();
- Line l2(b,a);
- l2.draw();
- // std::vector<Shape> Shapes; // ERROR! Shape is an abstract class!
- std::vector<Shape*> Shapes; // OK
- // Shapes.push_back( l1 ) ; // ERROR! std::vector won't allow a Line in place of a Shape
- std::cerr << "TYPE &l1:\t" << typeid(&l1).name() << std::endl;
- sp = std::addressof(l1); // works because a Line ISA Shape
- Shapes.push_back( sp ); // add contents by quick pointer
- sp = std::addressof(l2);
- Shapes.push_back( sp ); // (Shape*)
- sp = NULL; // no dangling pointers
- // Shapes.back()->draw();
- for ( auto* s : Shapes) // returns i as (Shape*)
- {
- rotate(s, R_ANGLE ); // safe rotate
- s->draw(); // calls (Line*)->draw()
- }
- return 0;
- }
- int main()
- {
- int r = test3();
- return r;
- }
- /*END*/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement