$CPP directive

Purpose:

$CPP directive attempts C++ compliance in the translated code. Translated code is output to a file named with a .cpp extension. This directive is equivalent to using the BCX command line -c flag.

Example:

$CPP ' Turn on C++ features and use .cpp filename extension

TestSub(1)
TestSub(1, 2)
TestSub(1, 2, 3)

SUB TestSub OPTIONAL(a, b = 0, c = 0)
 PRINT
 IF a > 0 THEN PRINT a;
 IF b > 0 THEN PRINT b;
 IF c > 0 THEN PRINT c
 PRINT
END SUB

Result:

1

1 2

1 2 3

$CPPHDR directive

Purpose:

When the $CPPHDR directive is used, with the $CPP directive, the BCX translator automatically adds the following code to the .cpp file translation produced by BCX.

// Additional lines may be needed
#if defined(__cplusplus)
  #include <iostream>
  #include <fstream>
  #include <sstream>
  #include <iomanip>
  typedef std::string stdstr;
#endif

To add other Standard C++ Library header files to the BCX code to be translated use the directive

#include <Filename>

$NOIO directive

Purpose:

The $NOIO directive disables the inclusion of the <iostream> C++ header file.

By default, when the $CPPHDR directive is used, the BCX translation includes the <iostream> C++ header file. You can disable the inclusion by adding the line $NOIO to your BCX program source code, placing it immediately after the $CPPHDR directive.

The C++ Tutorial for BCX Users

With the permission of the original author Eric Brasseur, the following is an abridged BCX version of Brasseur's "The C++ tutorial for C users".

Chapter 3. Console input and output streams

Input from the keyboard and output to the screen can be performed through std::cin >> and std::cout <<.

Example C03.bas:

$CPP

#INCLUDE <iostream>

FUNCTION MAIN()

  DIM age%
  DIM name$
        
  std::cout << "This is a sample program." << std::endl
        
  std::cout << std::endl   ' Just a line feed (end of line)
  
  std::cout << "Type your age : "
  std::cin >> age%
        
  std::cout << "Type your name: "
  std::cin >> name$
        
  std::cout << std::endl
        
  std::cout << "Hello " << name$ << " you're " << age% << " years old." << std::endl
  std::cout << std::endl << std::endl << "Bye!" << std::endl

END FUNCTION

Result:

This is a sample program.

Type your age : 24
Type your name: BCX

Hello BCX you're 24 years old.

Bye!

Chapter 7. Global variables

A global variable can be accessed even if another variable with the same name has been declared inside the function.

Example C07.bas:

$CPP

#INCLUDE <iostream>
 
DIM a# = 128
   
FUNCTION MAIN ()
  
  DIM a# = 256
      
  std::cout << "Local a:  " << a#   << std::endl
  std::cout << "Global a: " << ::a# << std::endl
   
END FUNCTION

Result:

Local a:  256
Global a: 128

Chapter 8. Reference

It is possible to make one variable be another.

Example C08_01.bas:

$CPP

#INCLUDE <iostream>

FUNCTION MAIN ()

  DIM a# = 3.1415927
     
  DIM AS DOUBLE &b = a      ' b is a#
  
  b = 89
     
  std::cout << "a# contains: " << a# << std::endl

END FUNCTION

Result:

a# contains: 89

If you are used to pointers and absolutely want to know what happens, simply think

DOUBLE &b = a

is translated to

DOUBLE *b = &a

and all subsequent b are replaced by *b.

The value of reference b cannot be changed after its declaration. For example you cannot write, a few lines further,

&b = c

expecting that b is now c. It won't work. Everything is said on the declaration line of b. Reference b and variable a are married on that line and nothing will separate them.

References can be used to allow a function to modify a calling variable

Example C08_02.bas:

$CPP

#INCLUDE <iostream>
   
SUB Change (r AS DOUBLE &, s AS DOUBLE)
  r = 100
  s = 200
END SUB

FUNCTION MAIN ()

  RAW AS DOUBLE k, m
    
  k = 3
  m = 4
    
  CALL Change(k, m)
    
  std::cout << k << ", " << m << std::endl

END FUNCTION

Result:

100, 4

A reference can be used to let a function return a variable.

Example C08_03.bas:

$CPP

#INCLUDE <iostream>
   
FUNCTION biggest (r AS DOUBLE &, s AS DOUBLE &) AS DOUBLE &
  IF r > s THEN FUNCTION = r
  FUNCTION = s
END FUNCTION

FUNCTION MAIN ()

  RAW AS DOUBLE k = 3
  RAW AS DOUBLE m = 7
    
  std::cout << "k: " << k << std::endl
  std::cout << "m: " << m << std::endl
  std::cout << std::endl
    
  biggest(k, m) = 10
    
  std::cout << "k: " << k << std::endl
  std::cout << "m: " << m << std::endl
  std::cout << std::endl
    
  biggest(k, m) ++
    
  std::cout << "k: " << k << std::endl
  std::cout << "m: " << m << std::endl
  std::cout << std::endl

END FUNCTION

Result:

k: 3
m: 7

k: 3
m: 10

k: 3
m: 11

Chapter 9. Namespace

Namespaces can be declared. The variables declared within a namespace can be used thanks to the :: operator

Example C09a.bas:

$CPP
  
#INCLUDE <iostream>
    
NAMESPACE first
  RAW AS INT a
  RAW AS INT b
END NAMESPACE
    
NAMESPACE second
  RAW AS DOUBLE a
  RAW AS DOUBLE b
END NAMESPACE
 
FUNCTION MAIN ()
        
  first::a = 2
  first::b = 5
        
  second::a = 6.453
  second::b = 4.1e4
        
  std::cout << first::a + second::a << std::endl
  std::cout << first::b + second::b << std::endl
 
END FUNCTION

Result:

8.453
41005

Example C09b.bas:

This example has a more BASIC-like feel to it.

$CPP

NAMESPACE first
  DIM ABC AS STRING ' ABC is a string 
END NAMESPACE

NAMESPACE second
  DIM ABC AS DOUBLE ' ABC is a double precision number 
END NAMESPACE


SUB MAIN

  first::ABC$ = "Here comes PI:"
  PRINT first::ABC$           ' $ sigil is required 

  second::ABC = ATN(1) * 4
  PRINT second::ABC#          ' # sigil is required 

  PAUSE
END SUB

Chapter 10. Inline

If a function contains just simple lines of code, doesn't use for loops or the like, it can be declared inline. This means its code will be inserted everywhere the function is used. That's somewhat like a macro. The main advantage is the program will be faster. A small drawback is it will be bigger, because the full code of the function was inserted everywhere it is used.

Example C10.bas:

$CPP

#INCLUDE <iostream>
   
inline FUNCTION hypothenuse (a AS DOUBLE, b AS DOUBLE) AS DOUBLE
 
FUNCTION = SQRT(a * a + b * b)
END FUNCTION

FUNCTION MAIN ()
   
  RAW AS DOUBLE k = 6, m = 9
    
  ' Next two lines produce exactly the same result:
  
  std::cout << hypothenuse(k, m) << std::endl
  std::cout << SQRT(k * k + m * m) << std::endl

END FUNCTION

Result:

10.8167
10.8167

Chapter 11. Exception

You know the classical control flow structures of C: FOR, IF, DO, WHILE ... C++ adds one more control structure named 'exception' consisting of members TRY, THROW, CATCH, and END TRY

Example C11.bas:

$CPP

#INCLUDE <iostream>

FUNCTION MAIN ()
  
  DIM AS INT a, b, c
  
  std::cout << "Type a number: "
  std::cin >> a
  std::cout << std::endl
      
TRY
  IF a > 100 THEN THROW 100
  IF a < 10 THEN THROW 10
  c = a/3
  THROW  c
CATCH (result AS INT)
  std::cout << "Result is: " << result << std::endl
  b = result + 1
END TRY
      
std::cout << "b contains: " << b << std::endl
      
std::cout << std::endl
      
' another example of EXCEPTION use:
     
RAW zero []    = "zero" AS CHAR
RAW even []    = "even" AS CHAR
RAW notprime [] = "not prime" AS CHAR
RAW prime []   = "prime" AS CHAR
      
TRY
  IF a = 0 THEN THROW zero
  IF (a / 2) * 2 = a THEN THROW even
  XFOR INT i = 3 WHILE i <= SQRT((float)a) BY i++
   std::cout << "Testing " << i << std::endl
   IF (a / i) * i = a THEN THROW notprime
  XNEXT
  THROW prime
CATCH (conclusion AS CONST CHAR PTR)
  std::cout << "The number you typed is "<< conclusion << std::endl
END TRY
      
std::cout << std::endl

END FUNCTION

Result:

Type a number: 5

Result is: 10
b contains: 11

The number you typed is prime

Chapter 12. Default Parameter Arguments

It is possible to define default parameters for functions.

Example C12.bas:

$CPP

#INCLUDE <iostream>
 
FUNCTION test (a AS DOUBLE, b = 7 AS DOUBLE) AS DOUBLE
  FUNCTION = a - b
END FUNCTION

FUNCTION MAIN ()

std::cout << test(14, 5) << std::endl
std::cout << test(14) << std::endl

END FUNCTION

Result:

9
7

Chapter 13. Function Overload

One important advantage of C++ is the function overload. Several functions can be declared with the same name provided there is a difference in their parameter list. Different functions can have the same name provided something allows the compiler to distinguish between them: number of parameters, type of parameters...

Example C13.bas:

$CPP

#INCLUDE <iostream>
 
FUNCTION test (a AS DOUBLE, b AS DOUBLE) AS DOUBLE
  FUNCTION = a + b
END FUNCTION
 
FUNCTION test (a AS INT, b AS INT) AS INT
  FUNCTION = a - b
END FUNCTION

FUNCTION MAIN ()

RAW AS DOUBLE   m = 7,  n = 4
RAW AS INT      k = 5,  p = 3
 
std::cout << test(m, n) << " , " << test(k, p) << std::endl

END FUNCTION

Result:

11 , 2

Chapter 14. Operator Overload

Operator overloading can be used to redefine the basic symbolic operators for new kinds of parameters. The symbolic operators (+ - * / ...) can be defined for new data types.

Example C14.bas:

$CPP

#INCLUDE <iostream>
 
TYPE bcx_vector
  DIM AS DOUBLE x
  DIM AS DOUBLE y
END TYPE
  
FUNCTION operator*(a AS DOUBLE, b AS bcx_vector) AS bcx_vector
  DIM AS bcx_vector r
 
  r.x = a * b.x
  r.y = a * b.y
 
  FUNCTION = r
END FUNCTION

FUNCTION MAIN ()

DIM AS bcx_vector k, m  ' No need to type "struct bcx_vector"

k.x = 2               ' To be able to write
k.y = -1               ' k = bcx_vector (2, -1)

m = 3.1415927 * k       ' Magic!

std::cout << "(" << m.x << ", " << m.y << ")" << std::endl

END FUNCTION

Result:

(6.28319, -3.14159)

Chapter 15. Template function

Tired of defining the same function five times? One definition for *int* type parameters, one definition for *double* type parameters, one definition for *float* type parameters... Didn't you forget one type? What if a new data type is used? No problem: the C++ compiler can automatically generate every version of the function that is necessary! Just tell it how the function looks like by declaring a *template* function

Example C15.bas:

$CPP

#INCLUDE <iostream>
   
TEMPLATE <class ttype>
FUNCTION minimum (a AS ttype, b AS ttype) AS ttype
  RAW AS ttype r
   
  r = a
  IF b < a THEN r = b
   
  FUNCTION = r
END FUNCTION
END TEMPLATE

FUNCTION MAIN ()
       
  RAW AS INT i1, i2, i3
  i1 = 34
  i2 = 6
  i3 = minimum(i1, i2)
  std::cout << "Most little: " << i3 << std::endl
   
  RAW AS DOUBLE d1, d2, d3
  d1 = 7.9
  d2 = 32.1
  d3 = minimum(d1, d2)
  std::cout << "Most little: " << d3 << std::endl
  std::cout << "Most little: " << minimum(d3, 3.5) << std::endl

END FUNCTION

Result:

Most little: 6
Most little: 7.9
Most little: 3.5

Chapter 16. NEW and DELETE

The keywords NEW and DELETE can be used to allocate and deallocate memory. They are cleaner than the functions malloc and free from standard C.

Example C16.bas:

$CPP

#INCLUDE <iostream>

FUNCTION MAIN ()
     
RAW AS DOUBLE PTR d  ' d is a variable whose purpose
                     ' is to contain the address of a
                     ' zone where a double is located
    
d = NEW DOUBLE       ' new allocates a zone of memory
                     ' large enough to contain a double
                     ' and returns its address.
                     ' That address is stored in d.
    
*d = 45.3           ' The number 45.3 is stored
                     ' inside the memory zone
                     ' whose address is given by d.
    
std::cout << "Type a number: "
std::cin >> *d
     
*d = *d + 5
     
std::cout << "Result: " << *d << std::endl
     
DELETE d             ' delete deallocates the
                     ' zone of memory whose address
                     ' is given by pointer d.
                     ' Now we can no more use that zone.
         
d = NEW DOUBLE[15] ' allocates a zone for an array
                     ' of 15 doubles. Note each 15
                     ' double will be constructed.
                     ' This is pointless here but it
                     ' is vital when using a data type
                     ' that needs its constructor be
                     ' used for each instance.
    
d[0] = 4456
d[1] = d[0] + 567
     
std::cout << "Content of d[1]: " << d[1] << std::endl
     
DELETE [] d          ' delete [] will deallocate the
                     ' memory zone. Note each 15
                     ' double will be destructed.
                     ' This is pointless here but it
                     ' is vital when using a data type
                     ' that needs its destructor be
                     ' used for each instance (the ~
                     ' method). Using delete without
                     ' the [] would deallocate the
                     ' memory zone without destructing
                     ' each of the 15 instances. That
                     ' would cause memory leakage.
    
RAW AS INT n = 30
     
d = NEW DOUBLE[n]   ' new can be used to allocate an
                     ' array of random size.
XFOR INT i = 0 WHILE i < n BY i++
  d[i] = i
XNEXT
     
DELETE [] d
     
DIM AS CHAR PTR s
     
s = NEW CHAR[100]
     
s$ = "Hello!"
     
std::cout << s << std::endl
     
DELETE [] s

END FUNCTION

Result:

Type a number: 6
Result: 11
Content of d[1]: 5023
Hello!

Chapter 17. METHODS

In standard C a struct contains only data. In C++ a struct definition can also include functions. Those functions are owned by the struct and are meant to operate on the data of the struct. Those functions are called METHODS. The example below defines the method surface() on the struct vector.

Example C17.bas:

$CPP

#INCLUDE <iostream>
 
PPTYPE bcx_vector
  RAW AS DOUBLE x
  RAW AS DOUBLE y
 
  FUNCTION surface () AS DOUBLE
    RAW AS DOUBLE s
    s = x * y
    IF s < 0 THEN s = -s
    FUNCTION = s
  END FUNCTION
END PPTYPE

FUNCTION MAIN ()

RAW AS bcx_vector a
     
a.x = 3
a.y = 4
     
std::cout << "The surface of a: " << a.surface() << std::endl

END FUNCTION

Result:

The surface of a: 12

Chapter 18. CONSTRUCTOR and DESTRUCTOR

Very special and essential methods are the CONSTRUCTOR and DESTRUCTOR. They are automatically called whenever an instance of a CLASS is created or destroyed (variable declaration, end of program, *new*, *delete*...).

The CONSTRUCTOR will initialize the variables of the instance, do some calculations, allocate some memory for the instance, output some text... whatever is needed.

Here is an example of a class definition with two overloaded constructors

Example C18.bas:

$CPP

#INCLUDE <iostream>

CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  CONSTRUCTOR bcx_vector() ' same name as class
   x = 0
    y = 0
  END CONSTRUCTOR
  
  CONSTRUCTOR bcx_vector(a AS DOUBLE, b AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
END CLASS

FUNCTION MAIN ()

RAW AS bcx_vector k         ' bcx_vector () is called
 
std::cout << "bcx_vector k: " << k.x << ", " << k.y << std::endl << std::endl
  
RAW AS bcx_vector m(45, 2) ' bcx_vector (double, double) is called
 
std::cout << "bcx_vector m: " << m.x << ", " << m.y << std::endl << std::endl
  
k = bcx_vector(23, 2)     ' bcx_vector created, copied to k, then erased
 
std::cout << "bcx_vector k: " << k.x << ", " << k.y << std::endl << std::endl

END FUNCTION

Result:

vector k: 0, 0

vector m: 45, 2

vector k: 23, 2

Chapter 19. Complex classes

If you cast an object like a vector, everything will happen correctly. For example, if vector *k* contains *(4, 7)*, after the cast *m = k* the vector *m* will contain *(4, 7)* too. The values of k.x and k.y have simply been copied to m.x and m.y.

Now suppose you're playing with objects like the person class above. Those objects contain a pointer to a character string. If you cast the person object by writing *p = r* it is necesary that some function does the work to make *p* be a correct copy of *r*. Otherwise, p.name will point to the same physical character string as r.name. What's more, the former character string pointed to by p.name is lost and becomes a memory zombie. The result will be catastrophic: a mess of pointers and lost data. The methods that will do the job are the COPY CONSTRUCTOR and an overload of the = operator

Example C19.bas:

$CPP

#INCLUDE <iostream>
 
CLASS person
  PUBLIC:
  RAW AS CHAR PTR name
  RAW AS INT age
  
  CONSTRUCTOR person(n = "no name" AS CONST CHAR PTR, a = 0 AS INT)
    name = NEW CHAR[100]
    name$ = n$
    age = a
  END CONSTRUCTOR
  
  CONSTRUCTOR person(s AS CONST person &) ' The COPY CONSTRUCTOR
  name = NEW CHAR[100]
    name$ = s.name$
    age = s.age
  END CONSTRUCTOR
  
  FUNCTION operator= (s AS CONST person &) AS person& ' overload of =
    name$ = s.name$
    age = s.age
    FUNCTION = *this
  END FUNCTION
  
  DESTRUCTOR ~person()
    DELETE [] name
  END DESTRUCTOR
END CLASS
  
SUB modify_person (h AS person&)
  h.age += 7
END SUB
  
FUNCTION compute_person (h AS person) AS person
  h.age += 7
  FUNCTION = h
END FUNCTION

FUNCTION MAIN ()

RAW AS person p
    
std::cout << p.name << ", age " << p.age << std::endl << std::endl
 
' output: no name, age 0

RAW AS person k ("John", 56)
std::cout << k.name << ", age " << k.age << std::endl << std::endl
' output: John, age 56

p = k
std::cout << p.name << ", age " << p.age << std::endl << std::endl
' output: John, age 56

p = person("Bob", 10)
std::cout << p.name << ", age " << p.age << std::endl << std::endl
' output: Bob, age 10

' Neither the copy constructor nor the overload
' of = are needed for this operation that modifies
' p since just the reference towards p is passed to
' the function modify_person:
modify_person(p)
std::cout << p.name << ", age " << p.age << std::endl << std::endl
' output: Bob, age 17

' The copy constructor is called to pass a complete
' copy of p to the function compute_person. The
' function uses that copy to make its computations
' then a copy of that modified copy is made to
' return the result. Finally, the overload of = is
' called to paste that second copy inside k:
k = compute_person(p)

std::cout << p.name << ", age " << p.age << std::endl << std::endl
' output: Bob, age 17

std::cout << k.name << ", age " << k.age << std::endl << std::endl
' output: Bob, age 24

END FUNCTION

Result:

no name, age 0

John, age 56

John, age 56

Bob, age 10

Bob, age 17

Bob, age 17

Bob, age 24

The copy constructor allows your program to make copies of instances when doing calculations. It is a key method. During calculations, instances are created to hold intermediate results. They are modified, cast and destroyed without you being aware. This is why those methods can be useful even for simple objects (see Chapter 14.).

In all the examples above, the methods are defined inside the class definition. That automatically makes them inline methods.

Chapter 20. Method prototype

If a method cannot be inline, or you do not want it to be inline, or if you want the class definition to contain the minimum amount of information (or you simply want the usual separate .h header file and .cpp source code file), then you need only put the prototype of the method inside the class and define the method below the class (or in a separate .cpp source file).

Example C20.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  DIM FUNCTION surface () AS DOUBLE
END CLASS test1,test2
  
FUNCTION bcx_vector::surface() AS DOUBLE
  RAW AS DOUBLE s = 0
  
  XFOR DOUBLE i = 0 WHILE i < x BY i++
    s = s + y
  XNEXT
  
  FUNCTION = s
END FUNCTION
  
FUNCTION MAIN ()

  RAW AS bcx_vector k
  
  k.x = 4
  k.y = 5
  
  std::cout << "Surface: " << k.surface() << std::endl

END FUNCTION

Result:

Surface: 20

Chapter 21. this

When a method is applied to an instance, that method may use the instance's variables, modify them... But sometimes it is necessary to know the address of the instance. No problem, the keyword *this* is intended for that purpose.

Example C21.bas:

$CPP

#INCLUDE <iostream>
  
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
   
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
   
  FUNCTION module () AS DOUBLE
    FUNCTION = SQRT(x * x + y * y)
  END FUNCTION
   
  SUB set_length (a = 1 AS DOUBLE)
    RAW AS DOUBLE length
   
    length = this->module()
   
    x = x / length * a
    y = y / length * a
  END SUB
END CLASS

FUNCTION MAIN ()
 
  RAW AS bcx_vector c(3, 5)
   
  std::cout << "The module of bcx_vector c: " << c.module() << std::endl
   
  c.set_length(2)   ' Transforms c in a bcx_vector of size 2
  
  std::cout << "The module of bcx_vector c: " << c.module() << std::endl
   
  c.set_length()    ' Transforms b in an unitary bcx_vector.
  
  std::cout << "The module of bcx_vector c: " << c.module() << std::endl

END FUNCTION

Result:

The module of vector c: 5.83095
The module of vector c: 2
The module of vector c: 1

Chapter 22. Arrays

Of course, it is possible to declare arrays of objects.

Example C22.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
  
  FUNCTION module () AS DOUBLE
    FUNCTION = SQRT(x * x + y * y)
  END FUNCTION
END CLASS

FUNCTION MAIN ()

  RAW AS bcx_vector s[1000]
  
  RAW AS bcx_vector t[3] = {bcx_vector(4, 5), bcx_vector(5, 5), bcx_vector(2, 4)}
  
  s[23] = t[2]
  
  std::cout << t[0].module() << std::endl

END FUNCTION

Result:

6.40312

Chapter 23. Class declaration

Here is an example of a full class declaration.

Example C23.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  DIM CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
  DIM CONSTRUCTOR bcx_vector()
  
  DIM FUNCTION operator + (a AS bcx_vector) AS bcx_vector
  DIM FUNCTION operator - (a AS bcx_vector) AS bcx_vector
  DIM FUNCTION operator - () AS bcx_vector
  DIM FUNCTION operator * (a AS DOUBLE) AS bcx_vector
  DIM FUNCTION module () AS DOUBLE
  DIM SUB set_length (A  AS DOUBLE = 1)
END CLASS
  
CONSTRUCTOR bcx_vector::bcx_vector(a AS DOUBLE, b AS DOUBLE)
  x = a
  y = b
END CONSTRUCTOR
  
CONSTRUCTOR bcx_vector::bcx_vector()
  x = 0
  y = 0
END CONSTRUCTOR
  
FUNCTION bcx_vector::operator + (a AS bcx_vector) AS bcx_vector
  FUNCTION = bcx_vector(x + a.x, y + a.y)
END FUNCTION
  
FUNCTION bcx_vector::operator - (a AS bcx_vector) AS bcx_vector
  FUNCTION = bcx_vector(x - a.x, y - a.y)
END FUNCTION
  
FUNCTION bcx_vector::operator - () AS bcx_vector
  FUNCTION = bcx_vector(-x, -y)
END FUNCTION
  
FUNCTION bcx_vector::operator * (a AS DOUBLE) AS bcx_vector
  FUNCTION = bcx_vector(x * a, y * a)
END FUNCTION
  
FUNCTION bcx_vector::module() AS DOUBLE
  FUNCTION = SQRT(x * x + y * y)
END FUNCTION
  
SUB bcx_vector::set_length(a AS DOUBLE)
  RAW AS DOUBLE length = this->module()
  
  x = x / length * a
  y = y / length * a
END SUB
  
FUNCTION operator << (o AS std::ostream&, a AS bcx_vector) AS std::ostream&
  o << "(" << a.x << ", " << a.y << ")";
  FUNCTION = o
END FUNCTION
  
FUNCTION MAIN ()

  RAW AS bcx_vector a
  RAW AS bcx_vector b
  RAW AS bcx_vector c(3, 5)
  
  a = c * 3
  a = b + c
  c = b - c + a + (b - a) * 7
  c = -c
  
  std::cout << "The module of bcx_vector c: " << c.module() << std::endl
  
  std::cout << "The content of bcx_vector a: " << a << std::endl
  std::cout << "The opposite of bcx_vector a: " << -a << std::endl
  
  c.set_length(2)        ' Transforms c in a bcx_vector of size 2.
 
  a = bcx_vector(56, -3)
  b = bcx_vector(7, c.y)
  
  b.set_length()         ' Transforms b in an unitary bcx_vector.
 
  std::cout << "The content of bcx_vector b: " << b << std::endl
  
  DIM AS DOUBLE k
  k = bcx_vector(1, 1).module() ' k will contain 1.4142.
 std::cout << "k contains: " << k << std::endl

END FUNCTION

Result:

The module of bcx_vector c: 40.8167
The content of bcx_vector a: (3, 5)
The opposite of bcx_vector a: (-3, -5)
The content of bcx_vector b: (0.971275, 0.23796)
k contains: 1.41421

Chapter 23a. Class declaration with destructor.

A destructor has been added to the Chapter 23 example to show the use of the tilde "~" with destructors.

Example C23a.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  DIM CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
  DIM CONSTRUCTOR bcx_vector()
  'need ~ below
 DIM DESTRUCTOR ~bcx_vector()
  DIM FUNCTION operator + (a AS bcx_vector) AS bcx_vector
  DIM FUNCTION operator - (a AS bcx_vector) AS bcx_vector
  DIM FUNCTION operator - () AS bcx_vector
  DIM FUNCTION operator * (a AS DOUBLE) AS bcx_vector
  DIM FUNCTION module () AS DOUBLE
  DIM SUB set_length (A  AS DOUBLE = 1)
END CLASS
  
CONSTRUCTOR bcx_vector::bcx_vector(a AS DOUBLE, b AS DOUBLE)
  x = a
  y = b
END CONSTRUCTOR
  
CONSTRUCTOR bcx_vector::bcx_vector()
  x = 0
  y = 0
END CONSTRUCTOR
'need ~ below
DESTRUCTOR bcx_vector::~bcx_vector()
      
END DESTRUCTOR
  
FUNCTION bcx_vector::operator + (a AS bcx_vector) AS bcx_vector
  FUNCTION = bcx_vector(x + a.x, y + a.y)
END FUNCTION
  
FUNCTION bcx_vector::operator - (a AS bcx_vector) AS bcx_vector
  FUNCTION = bcx_vector(x - a.x, y - a.y)
END FUNCTION
  
FUNCTION bcx_vector::operator - () AS bcx_vector
  FUNCTION = bcx_vector(-x, -y)
END FUNCTION
  
FUNCTION bcx_vector::operator * (a AS DOUBLE) AS bcx_vector
  FUNCTION = bcx_vector(x * a, y * a)
END FUNCTION
  
FUNCTION bcx_vector::module() AS DOUBLE
  FUNCTION = SQRT(x * x + y * y)
END FUNCTION
  
SUB bcx_vector::set_length(a AS DOUBLE)
  RAW AS DOUBLE length = this->module()
  
  x = x / length * a
  y = y / length * a
END SUB
  
FUNCTION operator << (o AS std::ostream&, a AS bcx_vector) AS std::ostream&
  o << "(" << a.x << ", " << a.y << ")";
  FUNCTION = o
END FUNCTION
  
FUNCTION MAIN ()

  RAW AS bcx_vector a
  RAW AS bcx_vector b
  RAW AS bcx_vector c(3, 5)
  
  a = c * 3
  a = b + c
  c = b - c + a + (b - a) * 7
  c = -c
  
  std::cout << "The module of bcx_vector c: " << c.module() << std::endl
  
  std::cout << "The content of bcx_vector a: " << a << std::endl
  std::cout << "The oposite of bcx_vector a: " << -a << std::endl
  
  c.set_length(2)        ' Transforms c in a bcx_vector of size 2.
 
  a = bcx_vector(56, -3)
  b = bcx_vector(7, c.y)
  
  b.set_length()         ' Transforms b in an unitary bcx_vector.
 
  std::cout << "The content of bcx_vector b: " << b << std::endl
  
  DIM AS DOUBLE k
  k = bcx_vector(1, 1).module() ' k will contain 1.4142.
 std::cout << "k contains: " << k << std::endl

END FUNCTION

Result:

The module of bcx_vector c: 40.8167
The content of bcx_vector a: (3, 5)
The opposite of bcx_vector a: (-3, -5)
The content of bcx_vector b: (0.971275, 0.23796)
k contains: 1.41421

Chapter 24. Static variables

One or more variables in a class can be declared *static*. In which case, only one instance of those variables exist, shared by all instances of the class. It must be initialised outside the class declaration.

Example C24.bas:

$CPP

#INCLUDE <iostream>

CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  STATIC AS INT count
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
    count++
  END CONSTRUCTOR
  
  DESTRUCTOR ~bcx_vector()
    count--
  END DESTRUCTOR
END CLASS
  
RAW AS INT bcx_vector::count = 0

FUNCTION MAIN ()

  std::cout << "Number of bcx_vectors:" << std::endl
  
  RAW AS bcx_vector a
  std::cout << bcx_vector::count << std::endl
  
  RAW AS bcx_vector b
  std::cout << bcx_vector::count  << std::endl
  
  RAW AS bcx_vector PTR r, u
  
  r = NEW bcx_vector
  std::cout << bcx_vector::count << std::endl
  
  u = NEW bcx_vector
  std::cout << a.count << std::endl
  
  DELETE r
  std::cout << bcx_vector::count << std::endl
  
  DELETE u
  std::cout << b.count << std::endl

END FUNCTION

Result:

Number of bcx_vectors:
1
2
3
4
3
2

Chapter 25. Const variables

A class variable can also be *const*ant. That's just like static, except it is given a value inside the class declaration and that value cannot be modified.

👉 A const within a CLASS must be a static const of integral or enumeration type initialized by a constant expression.

Some compilers, for example g++, have an extension that allows for const static float and doubles within a CLASS. Also, the C++0x standard has introduced a new type, constexpr, which allows in-CLASS float and doubles constants.

Example C25.bas:

$CPP

#INCLUDE <iostream>

CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  CONST STATIC INT piint = 3
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
  
  FUNCTION cylinder_volume () AS DOUBLE
    FUNCTION = x * x / 4 * (piint + 0.14159) * y
  END FUNCTION
END CLASS

FUNCTION MAIN ()

  std::cout << "The value of pi: " << bcx_vector::piint + 0.14159 << std::endl << std::endl
  
  bcx_vector k(3, 4)
  
  std::cout << "Result: " << k.cylinder_volume() << std::endl

END FUNCTION

Result:

The value of pi: 3.14159

Result: 28.2743

Chapter 26. Derived Class.

A class can be derived from another class. The new class inherits the variables and methods of the base class. Additional variables and/or methods can be added.

Example C26.bas:

$CPP

#INCLUDE <iostream>

CLASS bcx_vector
  PUBLIC:
  
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
  
  FUNCTION module () AS DOUBLE
    FUNCTION = SQRT(x*x + y*y)
  END FUNCTION
  
  FUNCTION surface () AS DOUBLE
    FUNCTION = x * y
  END FUNCTION
END CLASS
  
CLASS trivector USING PUBLIC bcx_vector    ' trivector is derived from bcx_vector
 PUBLIC:
  RAW AS DOUBLE z       ' added to x and y from bcx_vector
 
  CONSTRUCTOR trivector(m=0 AS DOUBLE, n=0 AS DOUBLE, p=0 AS DOUBLE) USING bcx_vector(m, n)
    z = p               ' bcx_vector constructor will
  END CONSTRUCTOR       ' be called before trivector
                        ' constructor, with parameters
                        ' m and n.
 
  CONSTRUCTOR trivector(a AS bcx_vector) ' What to do if a bcx_vector is cast to a trivector
   x = a.x
    y = a.y
    z = 0
  END CONSTRUCTOR
  
  FUNCTION module () AS DOUBLE      ' define module() for trivector
   FUNCTION = SQRT(x*x + y*y + z*z)
  END FUNCTION
  
  FUNCTION volume () AS DOUBLE
    FUNCTION = this->surface() * z    ' or x * y * z
 END FUNCTION
END CLASS
  
FUNCTION MAIN ()

  RAW AS bcx_vector a(4, 5)
  RAW AS trivector b (1, 2, 3)
  
  std::cout << "a (4, 5)    b (1, 2, 3)    *r = b" << std::endl << std::endl
  
  std::cout << "Surface of a: " << a.surface() << std::endl
  std::cout << "Volume of b: " << b.volume() << std::endl
  std::cout << "Surface of base of b: " << b.surface() << std::endl
  
  std::cout << "Module of a: " << a.module() << std::endl
  std::cout << "Module of b: " << b.module() << std::endl
  std::cout << "Module of base of b: " << b.bcx_vector::module() << std::endl
  
  RAW AS trivector k
  k = a                 ' thanks to trivector(bcx_vector) definition
 ' copy of x and y,       k.z = 0
 RAW AS bcx_vector j
  j = b                 ' copy of x and y.       b.z left out
 
  RAW AS bcx_vector PTR r
  r = &b
  
  std::cout << "Surface of r: " << r->surface() << std::endl
  std::cout << "Module of r: " << r->module() << std::endl

END FUNCTION

Result:

a (4, 5)    b (1, 2, 3)    *r = b

Surface of a: 20
Volume of b: 6
Surface of base of b: 2
Module of a: 6.40312
Module of b: 3.74166
Module of base of b: 2.23607
Surface of r: 2
Module of r: 2.23607

Chapter 27. Virtual method

In the Chapter 26 example, *r->module()* calculates the vector module, using *x* and *y*, because *r* has been declared a vector pointer. The fact that *r* actually points to a trivector is not taken into account. If you want the program to check the type of the pointed object and choose the appropriate method, then you must declare that method as *virtual* inside the base class.

(If at least one of the methods of the base class is virtual then a "header" of 4 bytes is added to every instance of the classes. This allows the program to determine what a vector actually points to.) (4 bytes is probably implementation specific. On a 64 bit machine maybe it is 8 bytes...)

Example C27.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
  
  FUNCTION module () AS virtual DOUBLE
    FUNCTION = SQRT(x*x + y*y)
  END FUNCTION
END CLASS
  
CLASS trivector USING PUBLIC bcx_vector
  PUBLIC:
  RAW AS DOUBLE z
  
  CONSTRUCTOR trivector(m = 0 AS DOUBLE, n = 0 AS DOUBLE, p = 0 AS DOUBLE)
   x = m                   ' Just for the game,
   y = n                   ' here I do not call the bcx_vector
   z = p                   ' constructor and I make the
 END CONSTRUCTOR          ' trivector constructor do the
                           ' whole job. Same result.
 
  FUNCTION module () AS DOUBLE
    FUNCTION = SQRT(x*x + y*y + z*z)
  END FUNCTION
END CLASS
  
SUB test (k AS bcx_vector &)
  std::cout << "Test result:            " << k.module() << std::endl
END SUB
  
FUNCTION MAIN ()

  RAW AS bcx_vector a(4, 5)
  RAW AS trivector b (1, 2, 3)
  
  std::cout << "a (4, 5)    b (1, 2, 3)" << std::endl << std::endl
  
  RAW AS bcx_vector PTR r
  
  r = &a
  std::cout << "module of bcx_vector a: " << r->module() << std::endl
  
  r = &b
  std::cout << "module of trivector b:  " << r->module() << std::endl
  
  test(a)
  
  test(b)
  
  RAW AS bcx_vector &s = b
  
  std::cout << "module of trivector b:  " << s.module() << std::endl

END FUNCTION

Result:

a (4, 5)    b (1, 2, 3)

module of bcx_vector a: 6.40312
module of trivector b:  3.74166
Test result:            6.40312
Test result:            3.74166
module of trivector b:  3.74166

Chapter 28. Multi-class method

Maybe you wonder if a class can be derived from more than one base class. The answer is yes.

Example C28.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PUBLIC:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
  
  FUNCTION surface () AS DOUBLE
    FUNCTION = fabs(x * y)
  END FUNCTION
END CLASS
 
CLASS number
  PUBLIC:
  RAW AS DOUBLE z
  
  CONSTRUCTOR number(a AS DOUBLE)
    z = a
  END CONSTRUCTOR
  
  FUNCTION is_negative () AS INT
    IF z < 0 THEN
      FUNCTION = 1
    ELSE
      FUNCTION = 0
    END IF
  END FUNCTION
END CLASS
 
CLASS trivector USING PUBLIC bcx_vector, PUBLIC number
  PUBLIC:
  
  CONSTRUCTOR trivector(a=0 AS DOUBLE, b=0 AS DOUBLE, c=0 AS DOUBLE) USING bcx_vector(a,b), number(c)
  END CONSTRUCTOR ' The trivector constructor calls the bcx_vector
                    ' constructor, then the number constructor,
                    ' and in this example does nothing more.
 
  FUNCTION volume () AS DOUBLE
    FUNCTION = fabs(x * y * z)
  END FUNCTION
END CLASS
 
FUNCTION MAIN ()

  RAW AS trivector a(2, 3, -4)
  
  std::cout << a.volume() << std::endl
  std::cout << a.surface() << std::endl
  std::cout << a.is_negative() << std::endl

END FUNCTION

Result:

24
6
1

Chapter 29. Derived Class

Class derivation allows you to construct more complex classes built from base classes. There is another application of class derivation: allowing the programmer to write generic functions.

Suppose you define a base class with no variables. It makes no sense to use instances of that class inside your program. But then you write a function whose purpose it is to sort instances of that class. That function will be able to sort any type of object provided it belongs to a class derived from that base class! The only condition is that inside of each derived class definition, all methods that the sort function needs are correctly defined.

Example C29.bas:

$CPP

#INCLUDE <iostream>

CLASS octopus
  PUBLIC:
  
  IMPFUNCTION module() = 0 AS virtual DOUBLE
  ' = 0 implies function is not
  ' defined. This makes instances
  ' of this CLASS cannot be declared.
END CLASS
  
FUNCTION biggest_module (a AS octopus &, b  AS octopus &, c  AS octopus &) AS DOUBLE
  RAW r = a.module() AS DOUBLE
  IF b.module() > r THEN r = b.module()
  IF c.module() > r THEN r = c.module()
  FUNCTION = r
END FUNCTION
  
CLASS bcx_vector USING PUBLIC octopus
  PUBLIC:
  
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  CONSTRUCTOR bcx_vector(a = 0 AS DOUBLE, b = 0 AS DOUBLE)
    x = a
    y = b
  END CONSTRUCTOR
  
  FUNCTION module () AS DOUBLE
    FUNCTION = SQRT(x * x + y * y)
  END FUNCTION
END CLASS
  
CLASS number USING PUBLIC octopus
  PUBLIC:
  
  RAW AS DOUBLE n
  
  CONSTRUCTOR number(a = 0 AS DOUBLE)
    n = a
  END CONSTRUCTOR
  
  FUNCTION module () AS DOUBLE
    IF n >= 0 THEN FUNCTION = n
    FUNCTION = -n
  END FUNCTION
END CLASS
  
FUNCTION MAIN ()

  RAW AS bcx_vector k(1,2), m(6,7), n(100, 0)
  RAW AS number p (5),   q(-3),  r(-150)
  
  std::cout << biggest_module(k, m, n) << std::endl
  std::cout << biggest_module(p, q, r) << std::endl
  std::cout << biggest_module(p, q, n) << std::endl

END FUNCTION

Result:

100
150
100

Chapter 30. Encapsulation: public, protected and private

The *PUBLIC:* directive means the variables or the methods below can be accessed and used everywhere in the program.

If you want the variables and methods to be accessible only to methods of the class AND to methods of derived classes, then you must put the keyword *protected:* before them.

If you want variables or methods to be accessible ONLY to methods of the class, then you must put the keyword *private:* before them.

The fact that variables or methods are declared private or protected means that nothing external to the class can access or use them. That's ENCAPSULATION. (If you want to give a specific function the right to access those variables and methods, then you must include that function's prototype inside the class definition, preceded by the keyword *friend*.)

Good practice is to encapsulate all the variables of a class. This can sound strange if you're used to structs in C. Indeed a struct only makes sense if you can access its data... In C++ you have to create methods to access the data inside a class. The example below uses the basic example of chapter 17, yet declares the class data to be protected.

Example C30_01.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PROTECTED:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  PUBLIC:
  
  SUB set_x (n AS INT)
    x = n
  END SUB
  
  SUB set_y (n AS INT)
    y = n
  END SUB
  
  FUNCTION surface () AS DOUBLE
    RAW AS DOUBLE s
    s = x * y
    IF s < 0 THEN s = -s
    FUNCTION = s
  END FUNCTION
END CLASS
  
FUNCTION MAIN ()

  RAW AS bcx_vector a
  
  a.set_x(3)
  a.set_y(4)
  
  std::cout << "The surface of a: " << a.surface() << std::endl

END FUNCTION

Result:

The surface of a: 12

The example above is a bit odd since the class data x and y can be set but they cannot be read back. Any attempt in function main () to read a.x or a.y will result in a compilation error. In the next example, x and y can be read back.

Example C30_02.bas:

$CPP

#INCLUDE <iostream>
 
CLASS bcx_vector
  PROTECTED:
  RAW AS DOUBLE x
  RAW AS DOUBLE y
  
  PUBLIC:
  
  SUB set_x (n AS INT)
    x = n
  END SUB
  
  SUB set_y (n AS INT)
    y = n
  END SUB
  
  FUNCTION get_x () AS DOUBLE
    FUNCTION = x
  END FUNCTION
  
  FUNCTION get_y () AS DOUBLE
    FUNCTION = y
  END FUNCTION
  
  FUNCTION surface () AS DOUBLE
    RAW AS DOUBLE s
    s = x * y
    IF s < 0 THEN s = -s
    FUNCTION = s
  END FUNCTION
END CLASS
  
FUNCTION MAIN ()

  RAW AS bcx_vector a
  
  a.set_x(3)
  a.set_y(4)
  
  std::cout << "The surface of a: " << a.surface() << std::endl
  std::cout << "The width of a:   " << a.get_x() << std::endl
  std::cout << "The height of a:  " << a.get_y() << std::endl

END FUNCTION

Result:

The surface of a: 12
The width of a:   3
The height of a:  4

Chapter 31. Brief examples of file I/O

Let's talk about input/output. In C++ that's a very broad subject. Here is a program that writes to a file

Example C31_01.bas:

$CPP

#INCLUDE <iostream>
#INCLUDE <fstream>

FUNCTION MAIN ()

  RAW AS std::fstream f
  
  f.open("test.txt", std::ios::out)
  
  f << "This is a text output to a file." << std::endl
  
  RAW AS DOUBLE a = 345
  
  f  << "A number: " << a << std::endl
  
  f.close()
  std::cout << "test.txt created" << std::endl

END FUNCTION

Result:

test.txt created

The content of file test.txt should be

This is a text output to a file.
A number: 345

Before you use Example C31_02 be sure to create the test.txt file with Example C31_01.

Example C31_02.bas:

$CPP

#INCLUDE <iostream>
#INCLUDE <fstream>

FUNCTION MAIN ()

  RAW AS std::fstream f
  RAW AS CHAR c
  
  std::cout << "What's inside the test.txt file" << std::endl
  std::cout << std::endl
  
  f.open("test.txt", std::ios::in)
  
  WHILE NOT f.eof()
    f.get(c)   'Or c = f.get()
   std::cout << c
  WEND
  
  f.close()

END FUNCTION

Result:

What's inside the test.txt file

This is a text output to a file.
A number: 345

Chapter 32. Character arrays

Generally speaking, it is possible to do on character arrays the same operations as on files. This is very useful to convert data or manage memory arrays.

Here is a program that writes inside a character array.

Example C32_01.bas:

$CPP

#INCLUDE <iostream>
#INCLUDE <strstream>
   
FUNCTION MAIN ()

  RAW AS CHAR a[1024]
  RAW AS std::ostrstream b(a, 1024)
    
 b.seekp(0)                           ' Start from first char.
 b << "2 + 2 = " << 2 + 2 << std::ends ' ( ends, not std::endl )
                                       ' ends is simply the
                                       ' null character   '\0'
 std::cout << a << std::endl
    
  RAW AS DOUBLE v = 2
    
  a$ = "A sinus: "
    
  b.seekp(LEN(a))
  b << "sin (" << v << ") = " << SIN(v) << std::ends
    
  std::cout << a << std::endl

END FUNCTION

Result:

2 + 2 = 4
A sinus: sin (2) = 0.909297

A program that reads from a character string

Example C32_02.bas:

$CPP

#INCLUDE <iostream>
#INCLUDE <strstream>

FUNCTION MAIN ()

  RAW AS CHAR a[1024]
  RAW AS std::istrstream b(a, 1024)
 
  a$ = "45.656"
    
  RAW AS DOUBLE k, p
    
 b.seekg(0) ' Start from first character.
 b >> k
    
  k = k + 1
    
  std::cout << k << std::endl
    
  a$ = "444.23 56.89"
    
  b.seekg(0)
  b >> k >> p
    
  std::cout << k << ", " << p + 1 << std::endl

END FUNCTION

Result:

46.656
444.23, 57.89

Chapter 33. Formatted output

This program performs formatted output two different ways. Please note the *width()* and *setw()* MODIFIERS are only effective on the next item output to the stream. Subsequent items will not be influenced.

Example C33.bas:

$CPP

#INCLUDE <iostream>
#INCLUDE <iomanip>

FUNCTION MAIN ()

  RAW AS INT i
  
  std::cout << "A list of numbers:" << std::endl
  XFOR i = 1 WHILE i <= 1024 BY i *= 2
    std::cout.width(7)
    std::cout << i << std::endl
  XNEXT
  
  std::cout << "A table of numbers:" << std::endl
  XFOR i = 0 WHILE i <= 4 BY i++
    std::cout << std::setw(3) << i << std::setw(5) << i * i * i << std::endl
  XNEXT

END FUNCTION

Result:

A list of numbers:
     1
     2
     4
     8
    16
    32
    64
   128
   256
   512
  1024
A table of numbers:
 0    0
 1    1
 2    8
 3   27
 4   64

METHOD and PROPERTY keywords

METHOD is transformed to FUNCTION and PROPERTY is transformed to SUB prior to main BCX translation. METHOD and PROPERTY can only be used in a CLASS which can only be compiled with a C++ compiler in C++ mode.

Example:

$CPP

SUB MAIN
 DIM RAW AS bcx_vector a
 a.set_x(3)
 a.set_y(4)
 PRINT "The surface of a: ", a.surface#()
END SUB


CLASS bcx_vector
 PROTECTED:
 DIM RAW AS DOUBLE x
 DIM RAW AS DOUBLE y
 PUBLIC:

 PROPERTY set_x(n AS DOUBLE)
  x = n
 END PROPERTY

 PROPERTY set_y(n AS DOUBLE)
  y = n
 END PROPERTY

 METHOD surface() AS DOUBLE
  DIM RAW AS DOUBLE s
  s = x * y
  IF s < 0 THEN s = -s
  METHOD = s
 END METHOD
END CLASS

Result:

The surface of a:  12

PROPGET and PROPSET keywords

PROPGET is transformed to FUNCTION and PROPSET is transformed to SUB prior to main BCX translation. PROPGET and PROPSET can only be used in a CLASS which can only be compiled with a C++ compiler in C++ mode.

Example:

$CPP

OPTION BASE 1

DIM Vehicle [3] AS CAR

FOR INTEGER i = 1 TO 3
 '******************************************************************** 
 ' Every car starts out as a Black Volkswagon with 2 gallons of fuel 
 '******************************************************************** 
 PRINT "Vehicle:", i,     " is a ", _
 Vehicle[i].Get_Paint$(), " ",      _
 Vehicle[i].Get_Model$(), " with",  _
 Vehicle[i].Get_Fuel() , " gallons of fuel."
NEXT
PRINT

'*********************************************************************** 
'       Now let's trade our Volkswagons in on some muscle cars! 
'*********************************************************************** 

Vehicle[1].Set_Model("Chevrolet Corvette")
Vehicle[1].Set_Paint("White")
Vehicle[1].Set_Fuel  (10)

Vehicle[2].Set_Model("Ford Mustang")
Vehicle[2].Set_Paint("Blue")
Vehicle[2].Set_Fuel  (5)

Vehicle[3].Set_Model("Dodge Charger")
Vehicle[3].Set_Paint("Green")
Vehicle[3].Set_Fuel  (13)

FOR INTEGER i = 1 TO 3
 PRINT "Vehicle:", i,     " is a ", _
 Vehicle[i].Get_Paint$(), " ",      _
 Vehicle[i].Get_Model$(), " with",  _
 Vehicle[i].Get_Fuel(),   " gallons of fuel."
NEXT

PAUSE

'******************************************************************** 
'  Here is our CLASS named "CAR".  Each instance of "CAR" contains a 
'  CONSTRUCTOR, 3 variables, 3 "Setters", 3 "Getters" and a function 
'******************************************************************** 

CLASS CAR
 PROTECTED:
 DIM Model$, Paint$, Fuel   ' << These are our car PROPERTIES 

 '***************************************************************** 
 ' A CONSTRUCTOR is like a SUB that is called automatically when 
 ' a new instance of CAR is created.  CONSTRUCTOR names must use 
 ' the same name as the CLASS that it is stored within.  That is 
 ' why our CLASS and CONSTRUCTOR are both named "CAR".  That is 
 ' a C++ rule and therefore also a BCX rule. 
 '***************************************************************** 
 PUBLIC:
 CONSTRUCTOR CAR(M$ = "Volkswagon", P$ = "BLACK", F  = 2 )
  Model$ = M$
  Paint$ = P$
  Fuel   = F
 END CONSTRUCTOR

 '***************************************************************** 
 '              Here are our property "SETTERS" 
 '***************************************************************** 

 PROPSET Set_Model(M$)
  Model$  = M$
 END PROPSET

 PROPSET Set_Paint(c$)
  Paint$ = c$
 END PROPSET

 PROPSET Set_Fuel(x AS INTEGER)
  Fuel = x
 END PROPSET


 '***************************************************************** 
 '              Here are our property "GETTERS" 
 '***************************************************************** 

 PROPGET Get_Model() AS STRING
  PROPGET = MixCase$(Model$) ' Uses our helper function (below) 
 END PROPGET

 PROPGET Get_Fuel() AS INTEGER
  PROPGET = Fuel
 END PROPGET

 PROPGET Get_Paint() AS STRING
  PROPGET = MixCase$(Paint$) ' Uses our helper function (below) 
 END PROPGET

 '***************************************************************** 
 '             Here is a user-defined helper FUNCTION 
 '***************************************************************** 

 FUNCTION MixCase$ (A$)    ' Here is a local User-Defined Function 
  FUNCTION = MCASE$(A$) ' calling a built-in BCX string function 
 END FUNCTION

END CLASS

Result:

Vehicle: 1 is a Black Volkswagon with 2 gallons of fuel.
Vehicle: 2 is a Black Volkswagon with 2 gallons of fuel.
Vehicle: 3 is a Black Volkswagon with 2 gallons of fuel.

Vehicle: 1 is a White Chevy Corvette with 10 gallons of fuel.
Vehicle: 2 is a Blue Ford Mustang with 5 gallons of fuel.
Vehicle: 3 is a Green Dodge Charger with 13 gallons of fuel.

RBFOR, RBEXIT, and RBNEXT keywords

Purpose:

RBFOR, RBEXIT, and RBNEXT support C++range-based FOR ... NEXT loops. These require a C++11 or newer compiler.

Syntax:

RBFOR(DataType iteranger : iteratee)
Statements
[ RBEXIT RBFOR ]
RBNEXT

Parameters:

  • DataType data type of iterator. AUTO& data type inference by reference is preferred for the iteranger.
  • iteranger ordinal tracker of the iteratee.
  • iteratee an array, vector or any other construct that can iterated.

Remarks:

RBEXIT RBFOR can be used to exit the loop. Functionality is identical to EXIT FOR.

Example:

The following example iterates through the named directory, in this code the "C:\temp" directory, and prints any entries that have a .exe, .c, or .cpp extension.

👉 This example requires a compiler conforming, at minimum, to the C++17 standard.

$CPP
$CPPHDR

#INCLUDE <filesystem>

FUNCTION MAIN ()
  RAW AS std::filesystem::path p = "C:\temp"  ' Modify to suit your needs 
  DIM S1$
  IF std::filesystem::exists(p) THEN
    IF std::filesystem::is_directory(p) THEN
      RBFOR(AUTO& e : std::filesystem::recursive_directory_iterator(p))
      S1$ = e.path().string().c_str()  ' Be careful not to accidentally change case 
      IF (UCASE$(BCXSPLITPATH$(S1$, FEXT)) = ".EXE") OR _
         (UCASE$(BCXSPLITPATH$(S1$, FEXT)) = ".C")   OR _
         (UCASE$(BCXSPLITPATH$(S1$, FEXT)) = ".CPP") THEN
         PRINT S1$
      END IF
      RBNEXT
    ENDIF
  ELSE
    PRINT "Specified drive, path, and/or file extensions not found."
  END IF
  PRINT "ALL Done"
  PAUSE
END FUNCTION