Lesson of 23 July 2008¶
The Barton Nackman trick
File bntrick.cc
:open:
1#include <iostream>
2
3using namespace std ;
4
5/*
6 The Barton Nackman permit to use "virtual"
7 function without the overhead of the virtual table
8 dispatch.
9 The trick is based on the "derivation" of the base class
10 from its derived class by the use of template.
11 This circular definition is perfectly regular due to the
12 characteristic of C++ compiler which produce instance
13 of the code only when necessary.
14 In particular the base class is instaced only when the derived
15 class is (partially) defined.
16
17 */
18
19/*
20 * Classical method with VIRTUAL method
21 *
22 * A base class with some virtual methods
23 */
24
25class Base {
26
27public:
28 Base() {}
29 virtual ~Base() {}
30
31 virtual void a_method() = 0 ;
32
33} ;
34
35class Derived : public Base {
36
37public:
38 Derived() : Base() {}
39 virtual ~Derived() {}
40
41 virtual void a_method() {
42 cout << "a_method, Derived\n" ;
43 }
44
45} ;
46
47/*
48 The Barton Nackman trick
49*/
50
51/* define the base class parametrizing it with the
52 derived class */
53
54template <typename DERIVED>
55class BaseBN {
56
57public:
58 BaseBN() {}
59 ~BaseBN() {}
60
61 void
62 a_method() {
63 /*
64 * At this point the instruction
65 * static_cast<DERIVED*>(this)
66 * reinterpret the pointer this as a pointer to class DERIVED.
67 * This function is defined only when DERIVED is known
68 * and the call of the method a_method() is expanded inline
69 * so that a good compiler with a minimum of optimization
70 * produce a code without overhead.
71 *
72 */
73 return static_cast<DERIVED*>(this) -> a_method() ;
74 }
75} ;
76
77/* the derived class inherith from the base class
78 which is parametrized with the deribed class.
79 It seems a loop, but the compiler can solve it */
80
81class DerivedBN : public BaseBN<DerivedBN> {
82
83public:
84 DerivedBN() : BaseBN<DerivedBN>() {}
85 ~DerivedBN() {}
86
87 void
88 a_method() {
89 cout << "a_method, DerivedBN\n" ;
90 }
91
92} ;
93
94/*
95 * In usingBaseClass1 the class Base has a_method virtual so that
96 * the a_method() is the one of the derived class.
97 */
98
99void
100usingBaseClass1( Base & derived ) {
101 derived . a_method() ;
102}
103
104/*
105 * In usingBaseClass2 the class BaseBN has a_method NON virtual but
106 * the a_method() in base class use the template DERIVED to perform
107 * the dispatch call to the derived class method.
108 */
109
110template <typename DERIVED>
111void
112usingBaseClass2( BaseBN<DERIVED> & derived ) {
113 derived . a_method() ;
114}
115
116
117int
118main() {
119 DerivedBN derived2 ;
120 Derived derived1 ;
121
122 cout << "VIRTUAL CLASS\n" ;
123 derived1 . a_method() ;
124 usingBaseClass1( derived1 ) ;
125
126 cout << "NON VIRTUAL CLASS, STATIC POLIMORPHIS\n" ;
127 derived2 . a_method() ;
128 usingBaseClass2( derived2 ) ;
129
130}
Examples of calling C++ from FORTRAN
A supersimple parser written in C++
File parser.cc
:open:
1/*
2 A simple example of FORTRAN that call C++ code.
3 The interface pass to routine with C-link interface
4 in order that FORTRAN can call it.
5
6 The interface read from a file the data organized
7 as columns with the first line a headers
8
9 C1 C2 CN <- header line
10 V1 V2 V2 <- data line with numbers
11 V1 V2 V2 <- data line with numbers
12 V1 V2 V2 <- data line with numbers
13 V1 V2 V2 <- data line with numbers
14 ...
15
16*/
17
18#include <iostream>
19#include <fstream>
20#include <vector>
21#include <string>
22#include <sstream>
23
24using namespace std ;
25
26// normally fortran add _ (underscore) at the end of the name
27
28#define F77NAME(A) A##_
29
30//mappint of the type with fortran DOUBLE PRECISION and INTEGER
31typedef double double_precision ;
32typedef int integer ;
33
34/*
35 Declaration of the C-interface, FORTRAN callable.
36 */
37
38extern "C" {
39
40 // routine called to parse the file
41 void
42 F77NAME(parser_load_file)( char fileName[] ) ;
43
44 // ask for the readed dimension (for dynamical allocation or checl).
45 void
46 F77NAME(parser_get_dimension)( integer & numRows,
47 integer & numColumn ) ;
48 void
49 F77NAME(parser_get_data)( double_precision A[],
50 integer & ldA, // dimension of row of matrix A
51 char S[],
52 integer & ldS ) ;
53}
54
55
56/* memorize the read data to this vectors */
57
58static vector<vector<double_precision> > a ;
59static vector<string> strs ;
60
61extern "C"
62void
63F77NAME(parser_load_file)( char fileName[] ) {
64 // step 1: open the file
65 ifstream file(fileName) ;
66
67 // some check ...
68
69 // step 2: read the header
70 string line, str ;
71 getline( file, line ) ;
72
73 // wrap the readed line to the object lineStream of type
74 // istringstream. This object is a stream object the result
75 // is that now the string can be read as a file...
76 istringstream lineStream(line) ;
77
78 strs . clear();
79 while ( lineStream.good() ) {
80 lineStream >> str ;
81 if ( str . length() > 0 )
82 strs . push_back(str) ;
83 }
84
85 // step 2: read the data
86 a . resize(strs.size()) ;
87 while ( file.good() ) {
88 getline( file, line ) ;
89 // same trick as for the header
90 istringstream lineStream(line) ;
91 if ( line . length() > 0 ) {
92 for ( integer i = 0 ; i < strs.size() ; ++i ) {
93 double_precision bf ;
94 lineStream >> bf ;
95 a[i] . push_back(bf) ;
96 }
97 }
98 }
99 file . close() ;
100}
101
102extern "C"
103void
104F77NAME(parser_get_dimension)( integer & numRows,
105 integer & numColumn ) {
106 numRows = a[0].size() ;
107 numColumn = a.size() ;
108}
109
110extern "C"
111void
112F77NAME(parser_get_data)( double_precision A[],
113 integer & ldA, // dimension of row of matrix A
114 char S[],
115 integer & ldS ) {
116
117 integer numRows = a[0].size() ;
118 integer numColumn = a.size() ;
119
120 for ( integer j = 0 ; j < numColumn ; ++j )
121 for ( integer i = 0 ; i < numRows ; ++i )
122 A[i+j*ldA] = a[j][i] ; // j+i*ldA addess of the (i,j) in FORTRAN convection
123
124 fill( S, S+numColumn*ldS, ' ' ) ;
125 for ( integer j = 0 ; j < numColumn ; ++j ) {
126 copy( strs[j].begin(), strs[j].end(), S+j*ldS ) ;
127 //for ( integer k = 0 ; k < 100 ; ++k )
128 // cout << k << " " << (int)S[j*ldS+k] << endl ;
129 }
130}
A fortran driver program
File main.f
:open:
1C
2C A driver FORTRAN code to call the C++ code
3C
4 PROGRAM EXAMPLE
5
6 DOUBLE PRECISION A(100,100)
7 CHARACTER*100 fileName, str(100)
8 INTEGER numRows, numColumn
9
10 fileName = 'PIPPO.TXT' // CHAR(0)
11
12 CALL parser_load_file( fileName )
13
14 CALL parser_get_dimension( numRows, numColumn )
15 TYPE *, 'numRows=', numRows, ' numColumn=', numColumn
16
17 CALL parser_get_data( A, 100, str, 100 )
18
19 TYPE *,(TRIM(str(i)),' ',i=1,numColumn)
20
21 DO i=1,numRows
22 TYPE *,(A(i,j),j=1,numColumn)
23 END DO
24
25 END
Using STL string class
A simple program that convert number in letters
File numberToString.cc
:open:
1/*
2 Convert a number to a string.
3 For example
4
5 123456 = one hundred and twenty three thousads and four hundred and fifty six
6
7*/
8
9#include <iostream>
10#include <iomanip>
11#include <string>
12#include <vector>
13#include <numeric>
14
15using namespace std ;
16
17typedef int indexType ;
18
19void
20splitDigit( indexType number,
21 vector<indexType> & digits ) {
22 digits.clear() ;
23 while ( number > 0 ) {
24 digits . push_back(number % 10) ;
25 number /= 10 ;
26 }
27}
28
29// the first 20 number
30
31string num0to19[] = {
32 "",
33 "one",
34 "two",
35 "three",
36 "four",
37 "five",
38 "six",
39 "seven",
40 "eight",
41 "nine",
42 "ten",
43 "eleven",
44 "twelve",
45 "thirteen",
46 "fourteen",
47 "fifteen",
48 "sixteen",
49 "seventeen",
50 "eightteen",
51 "nineteen"
52} ;
53
54string num10to100[] = {
55 "",
56 "ten",
57 "twenty",
58 "thity",
59 "fourty",
60 "fifty",
61 "sixty",
62 "sevety",
63 "eighty",
64 "ninety"
65} ;
66
67
68// convert number from 0 to 999
69string
70num3digitToString( indexType num ) {
71 indexType n = num%100 ;
72 string res = "" ;
73 if ( n < 20 ) res = num0to19[n] ;
74 else res = num10to100[n/10]+" "+ num0to19[n%10] ;
75 indexType c = (num/100) % 10 ;
76 if ( c > 0 ) {
77 if ( n > 0 ) res = num0to19[c] + " hundred and " + res ;
78 else res = num0to19[c] + " hundred " ;
79 }
80 return res ;
81}
82
83// convert number collect 3 digits at iteration
84string
85numToString( indexType n ) {
86
87 // special case n = 0
88 if ( n == 0 ) return "zero" ;
89
90 string tmb[] = { " ", " thousads ", " millions ", " billions " } ;
91 string res = "" ;
92
93 for ( indexType k = 0 ; n > 0 ; n /= 1000, ++k ) {
94 string num = num3digitToString( n ) ;
95 if ( num . length() > 0 && res . length() > 0 ) res = "and " + res ;
96 if ( num . length() > 0 ) res = num + tmb[k] + res ;
97 }
98 return res ;
99}
100
101
102int
103main() {
104 vector<indexType> digits ;
105 indexType num = 123456 ;
106 splitDigit( num, digits ) ;
107 cout << "Split digits for " << num << "=" ;
108 copy( digits.begin(), digits.end(),
109 ostream_iterator<indexType>(cout, ":") ) ;
110
111 cout << '\n' ;
112 indexType nums[] = {100, 1000, 1000000, 123, 123456, 10000001, 0, -1 } ;
113 for ( indexType i = 0 ; nums[i] >= 0 ; ++i ) {
114 cout << setw(10) << nums[i] << " = " << numToString(nums[i]) << '\n' ;
115 }
116
117}
The lesson in a zip file
The zip file lesson9.zip