BASIC

Author Topic: A short story of OOP - INLINE, C++ and Classes  (Read 3825 times)

Offline Quentin

  • Prof. Inline
  • *****
  • Posts: 915
    • View Profile
(If you find extreme errors in my poor english please let me know)

Surely to compare this with Stephen Hawkings book about the time is overstated, but in my borderless modesty I couldn't catch something more adequated. ;)

As Gernot told us, after some questions about object orientated enhancements for GLBasic, that he wouldn't implement them and who really need this, should do it with INLINE, I took this words to my heart and tried to realize some OOP in GLBasic. One thing before we start: I'm not an expert in C++ and object orientated programming. So if you see some big violations against C++ or OOP dogmas, please tell it to me in a gentle way.

In principle GLBasic already works with classes. For example if you compile the following coding:


TYPE t_anything
   x
   y
ENDTYPE

LOCAL something AS t_anything


GLBasic will generate this C++ code.
(you'll find it in C:\Documents and Settings\<computername>\Local Settings\Temp\glbasic\gc_temp_class.h, please replace <computername> with your own one)


class t_anything
{
public:
   DGInt x;
   DGInt y;
   t_anything()
   {
      x = 0;
      y = 0;
   }
   t_anything(const t_anything& _in_)
   {*this = _in_;}
   t_anything& operator=(const t_anything& _in_)
   {
      this->x = _in_.x;
      this->y = _in_.y;
   return *this;
   }

};


A class was created from the GLBasic TYPE with the attributes x and y, two constructors to initialize the data and an overloaded = operator. The = operator must be overloaded to allow assignments like


LOCAL something AS t_anything
LOCAL something_new AS t_anything

something.x = 5
something.y = 7
something_new = something


The object creation of the class is done in the main file gpc_temp0.cpp within the function __MainGameSub__. The command is "REGISTER_VAR(t_anything, something);"
In principle all attributes are defined as "public". In order to change this we can define our own classes with INLINE.


So now we have the pattern to implement own classes. The following small example creates a class and plays something with the attributes.

Code: GLBasic [Select]
INLINE
// class definition
class canything
{
        private:
                DGInt x;
                DGInt y;
               
        public:
                // standard constructor
                canything()
                {      
                        x = 0;
                        y = 0;
                }

                DGInt getx() { return x; }
                DGInt gety() { return y; }
                void setpos(const DGInt new_x, const DGInt new_y)
                {
                        x = new_x;
                        y = new_y;
                }

};
ENDINLINE

INLINE
// create an object
canything something;
ENDINLINE

LOCAL scrx, scry, x, y
GETSCREENSIZE scrx, scry

WHILE TRUE
        INLINE
        //set x and y accidently
        something.setpos(RND(scrx), RND(scry));
        x = something.getx();
        y = something.gety();
        PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
        SHOWSCREEN();
        SLEEP(1500);
        ENDINLINE
WEND
 

Ok this works. Of course it is not very sensible.
In this example the functions (or methods) are defined within the class definition. If doing so, the class methods are defind implicit as inline methods (as far as I know). This is no handicap for this example. But if a method is bigger and is called several times in the coding it could bloat the final coding. We can try to change our class definition, so that within the class only the method headers are shown and the implementation outside ...

Code: GLBasic [Select]
INLINE
// class definition
class canything
{
        private:
                DGInt x;
                DGInt y;
               
        public:
                canything();
                DGInt getx();
                DGInt gety();
                void setpos(const DGInt new_x, const DGInt new_y);
};

canything::canything()
{
        x = 0;
        y = 0;
}

DGInt canything::getx() { return x; }
DGInt canything::gety() { return y; }
void canything::setpos(const DGInt new_x, const DGInt new_y)
{
        x = new_x;
        y = new_y;
}
ENDINLINE
 

... and we're facing a bad surprise. The compiler tells us several error messages, which are not very understandable on the fist view. On the second view (in my case the second view came from Gernot) the reason for the error messages are more clear. The implementation of the class methods was done within the main function of the coding, which is generated by GLBasic, more detailed, within the function __MainGameSub__, which is called from the WinMain function. Thus the class methods are implemented within a function and I don't know a compiler which allows that.

So I'll have to find another way to make the class definition global. You'll find several examples in the forum on how to do this.


// empty function in order to end the main function
FUNCTION __dummy:
ENDFUNCTION

INLINE
// here we can define our class
ENDINLINE


Now we're facing the next problem. Now the class is outside any function, but anyhow it isn't known within the main function. It's because the class def. is set AFTER the main function within the C++ coding which is generated by GLBasic. We can manage this by  creating our own main function, which is defined after INLINE. Schematically our coding looks as follows:


// call our own main function
MyMain()

// end original main function here
FUNCTION __dummy:
ENDFUNCTION

// class definition and implementation
INLINE
class...
ENDINLINE

// own main function
FUNCTION MyMain:
   ...
ENDFUNCTION



Once more the complete coding:

Code: GLBasic [Select]
// calling own main function
MyMain()


FUNCTION __dummy:
ENDFUNCTION

INLINE
// class definition
class canything
{
        private:
                DGInt x;
                DGInt y;
               
        public:
                canything();
                DGInt getx();
                DGInt gety();
                void setpos(const DGInt new_x, const DGInt new_y);
};

// Implementation of the class methods
canything::canything()
{
        x = 0;
        y = 0;
}

DGInt canything::getx() { return x; }
DGInt canything::gety() { return y; }
void canything::setpos(const DGInt new_x, const DGInt new_y)
{
        x = new_x;
        y = new_y;
}
ENDINLINE

// ------------------------------------------------------------- //
// ---  MYMAIN  ---
// ------------------------------------------------------------- //
FUNCTION MyMain:

        INLINE
        // create an object
        canything something;
        ENDINLINE

        LOCAL scrx, scry, x, y
        GETSCREENSIZE scrx, scry

        WHILE TRUE
                INLINE
                // set x and y accidently
                something.setpos(RND(scrx), RND(scry));
                x = something.getx();
                y = something.gety();
                PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
                SHOWSCREEN();
                SLEEP(1500);
                ENDINLINE
        WEND

ENDFUNCTION // MYMAIN
 
    Fine. Now we have the knowledge for further mistakes. I mean arrays. Often not only one variable of a TYPE (respectively an object) is needed but an array. As we're programming in C++ now there will be several ways to realize that.

    • with C++ arrays (canything something[20]; )
    • with linked lists
    • with an own array class
    • or ... we are clever an are using the functionallity of GLBasic

We will extend the example above by creating an array of class canything. This can be done very simple with the command


DGArray<canything> arr_something;


as described in the GLBasic help fle, DGArray is a template class. Thus we'll have to tell it which kind of array we want (here <canything>). With this array we can use all array functions of GLBasic like DIM, REDIM, DIMPUSH and DIMDEL.

Now here the changed main function:

Code: GLBasic [Select]
// ------------------------------------------------------------- //
// ---  MYMAIN  ---
// ------------------------------------------------------------- //
FUNCTION MyMain:

        LOCAL i
       
        INLINE
        // create an object
        canything something;
        DGArray<canything> arr_something;
        ENDINLINE

        LOCAL scrx, scry, x, y
        GETSCREENSIZE scrx, scry

        // create 10 elements
        FOR i = 0 TO 9
                INLINE
                something.setpos(RND(scrx), RND(scry));
                DIMPUSH(arr_something, something);
                ENDINLINE
        NEXT
       
        WHILE TRUE
                INLINE
                // display array in a FOREACH loop
                for(i = 0; i <= LEN(arr_something()) - 1; )
                {
                        canything& a = arr_something(i++);
                        x = a.getx();
                        y = a.gety();
                        PRINT(CGStr("X:") + x + CGStr(" - Y:") + y, x, y);
                }
                SHOWSCREEN();
                ENDINLINE
        WEND

ENDFUNCTION // MYMAIN
 

One word to the FOREACH loop within WILE-WEND. I've "learned" this by looking on how GLBasic is translating a FOREACH loop in C++ coding. You see that the loop counter "i" is not incremented in the for statement but later within the loop. This is important. Otherwise the loop will start with the second element and will cause an error when trying to process the last element which is out of bounds of the array.

The command


canything& a = arr_something(i++);


give us the address of the current array element in the varialbe a, so we can call all class methods with it. The DELETE statement of GLBasic will be translated to the commands


DIMDEL(arr_something, --i);
continue;


Ok, enough for now.

Result:
It made a lot of fun working with INLINE and classes. If it make sense ... I really don't know. Ok we'll have encapsulation of the data but there are so many traps when programming in C++ and it makes much more effort working in that way comparing the programming with GLBasic only. Anyway at the moment I enjoy playing with it.

« Last Edit: 2008-Aug-14 by Quentin »

Offline bigsofty

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 2631
    • View Profile
Nice tutorial Quentin, many thanks!  :good:

Quote
...there are so many traps when programming in C++ and it makes much more effort working in that way comparing the programming with GLBasic only
... ahem, just a little bit!  :whistle:
Cheers,

Ian.

“It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration.”
(E. W. Dijkstra)

Offline Quentin

  • Prof. Inline
  • *****
  • Posts: 915
    • View Profile
*ggg* yes bigsofty, the more we learn about C++ the more confused we are :enc: