1

I managed to convert an over 5000 line code of Fortran 77 program to C++ manually but the conversion didn't go according to plan. So I am trying to debug the C++ program using my Fortran 77 program. In fortran I developed a subroutine that takes an array and prints out the array index and its value into a comma delimited file. I am trying to do the similar thing in C++. but run foul of the "double temp1[tempi]" declaration. The array does not have to be the same size in all the calls to the function. So I cant code it a say "double temp1[21]" because the next time it is 25. Fortran passes arrays by reference. What do you propose I do.

I managed to to this for the Fortran program. The idea is to take the variable memory dump from the c++ program and compare the values in Excel using vba to see which one changes the most and then to focus on that variable in the C++ program a starting debugging point.

c++ code logic:

 void singlearrayd(double temp1[tempi], int tempi, string str1){
   for (int md_i = 1; md_i <= tempi; md_i++){
   cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
   }
 }

 int main(){
   double askin[22];
   double fm[26];
   singlearrayd(askin,22,"askin");
   singlearrayd(fm,26,"fm");
   return 0;
 }

Fortran 77 code logic:

 PROGRAM PRINT_MEMORY
 real*8 :: ASKIN(21)
 real*8 :: FM(25)
 CALL SINGLEARRAYD(ASKIN,21,"ASKIN")
 CALL SINGLEARRAYD(FM,25,"FM")
 END PRINT_MEMORY

 SUBROUTINE SINGLEARRAYD(TEMP1,TEMPI,STR1)
 IMPLICIT NONE
 CHARACTER(LEN=*) :: STR1
 INTEGER*4 MD_I,TEMPI
 REAL*8, DIMENSION(1:TEMPI) :: TEMP1
 DO MD_I = 1, TEMPI
 WRITE(51,'(ES25.16E3,A1,A25,A1,I5,A1)') TEMP1(MD_I),',',STR1,'(',
1 MD_I,')'
 ENDDO
 ENDSUBROUTINE SINGLEARRAYD
bjdesa
  • 63
  • 2
  • 9
  • 2
    You do not need to specify the size of the array. [Arrays decay to pointers](https://stackoverflow.com/questions/1461432/what-is-array-decaying) so the size information is lost anyway. `double temp1[]` should fix you right up. – user4581301 Apr 28 '19 at 06:20
  • 2
    its `double *temp1` or `double temp1[]` – Stephen Quan Apr 28 '19 at 06:21
  • 6
    Side note: Arrays in C and C++ are origin zero. That means `for (int md_i = 1; md_i <= tempi; md_i++)` starts one too late and runs one past the end of the array. Use `for (int md_i = 0; md_i < tempi; md_i++)` – user4581301 Apr 28 '19 at 06:21
  • 4
    Slightly unrelated (C++ programming): you do not need (and should not) declare md_i before the loop and consider using a std::vector instead of an array - the vector has a size()-method. – Gerriet Apr 28 '19 at 06:22
  • Array indexing in Fortran starts at 1. Array indexing in C++ starts at zero. Your loops are using Fortran indexing in C++ - a classic "off by one" error that will result in accessing array elements outside the range of valid elements. You need to correct that. – Peter Apr 28 '19 at 06:24
  • 1
    `std::array` is also helpful as a smart array if you know the size of the array at compile time. You'll have to pass it by reference, though because it won't decay. – user4581301 Apr 28 '19 at 06:24
  • 2
    If you don't know the size at compile-time then use [`std::vector`](https://en.cppreference.com/w/cpp/container/vector). – Some programmer dude Apr 28 '19 at 06:30
  • 1
    Also, your array `fm` in the C++ program is the wrong size. – Some programmer dude Apr 28 '19 at 06:31
  • okay if I were to try an extend this to call doublearrayd(clomsk,10,12,"clomsk") call doublearrayd(alpha,12,21,"alpha") would I have to define two new function instead of one because C++ requires me to input the second rank i.e. clomsk[][12]; and alpha[][21] would be the arguments of two new functions? Is there a way for me to use just one function to handle both cases? – bjdesa Apr 28 '19 at 14:41

3 Answers3

4

There are multiple problems in your code.

In C++, a native array (like your askin in main()) is converted into a pointer when passed to a function. So there is no need to declare a dimension on the array in the argument list BUT it is still necessary to pass a second argument, as you are specifying the size.

This means the C++ function should have the form

void singlearrayd(double temp1[], int tempi, std::string str1)

or (equivalently)

void singlearrayd(double *temp1, int tempi, std::string str1)

Note in the above that I have specified the type of the third argument by its full name as std::string. In a lot of cases, it is better avoid using namespace std.

The second problem is that you are assuming Fortran array indexing and C++ array indexing are the same. In reality, Fortran array indexing is 1-based (the first element of an array has index one, by default) and C++ array indexing is 0-based (the first element on an array has index zero). Using Fortran array indexing in C++ causes undefined behaviour, because it will access elements outside the valid range.

The third (potential) problem is that your function defines two variables named md_i (one in the function, and one within the loop). It is better to avoid doing that.

Addressing all of the above will turn your function to (in full)

void singlearrayd(double temp1[], int tempi, std::string str1)
{
    for (int md_i = 0; md_i < tempi; ++md_i)    // note the differences here carefully
    {
        cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
    }
 }

The fourth problem is that main() in C++ returns int, not void.

The fifth problem is that main() does not initialize the arrays before singlearrayd() prints them. In Fortran, arrays that are local to a function are (often) zero-initialised. In C++, they are uninitialised by default, so accessing their values (e.g. to print them) gives undefined behaviour.

int main()
{
   double askin[21] = {0.0};   // initialise the first element.  Other elements are initialised to zero
   double fm[21] = {0.0};
   singlearrayd(askin,21,"askin");
   singlearrayd(fm,25,"fm");
}

That will get your code working. Practically, however, there are improvements possible. The first improvement is to use a standard container rather than an array. Standard containers know their size, so that allows simplifying your function. Second, pass non-trivial arguments (like containers or strings) by reference - and preferably const reference if no change is being made to the argument. Unlike Fortran, where function arguments are often passed by reference BY DEFAULT, it is necessary to DELIBERATELY introduce references in C++.

#include <vector>

void singlearrayd(const std::vector<double> &temp1, const std::string &str1)
{
    for (std::size_t md_i = 0; md_i < temp1.size(); ++md_i)
   {
        cout << temp1[md_i] << "," << str1 << "(" << md_i << ")";
   }
}

int main()
{
   std::vector<double> askin(21);   // askin has 21 elements, initialised to zero
   std::vector<double> fm(21);
   singlearrayd(askin, "askin");
   singlearrayd(fm, "fm");
}

C++ containers also support iterators - which are safer in practice AND often more efficient - than using array indexing. I'll leave it as an exercise for you to learn how to use those.

A key message however: don't assume that a simple mechanical translation from Fortran to C++ will work. You have already demonstrated pitfalls of such an assumption. Take the time to learn C++ BEFORE trying to translate too much code from Fortran to C++. That is necessary both to get the C++ code working correctly and also to get it running efficiently.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • Peter just a follow up my code conversion was 100% successful after debugging it a day. Made some silly conversion mistakes translating from Fortran to C++. found one interesting C++ issue mainly for loop integer variables and outside the for loop was using the same variable name. Fortran allows one declaration that can be used and reused in do loop or outside them. But the overall process was a walk in the park. Thanks for your advice and suggestions. :) – bjdesa May 01 '19 at 04:33
2

A more modern implementation would be

 #include <string>
 #include <array>
 #include <iostream>

 template <std::size_t size, class U>
 void singlearrayd(const std::array<U, size>& temp1, const std::string& str1){
   int i = 0;
   for (const auto& x : temp1)
      std::cout << x << "," << str1 << "(" << (i++) << ")";
   }

 int main(){
   std::array<double, 21> askin;
   std::array<double, 21> fm; 
   singlearrayd(askin, "askin");
   singlearrayd(fm, "fm");

   return 0;
 }

Please note that in the code above the two arrays askin and fm are not initialized. Presumably, in the real code you would have already initialized them before calling singlarrayd. Also, remember that main must return an int.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • francesco thanks you for your help. The above code is a module I am building to help debug the program that I converted from Fortran. I figured the double temp1[] by my self before comments from everyone above my source is https://stackoverflow.com/questions/45220976/c-pass-a-reference-to-an-array-to-function. The example above is not the real code just something I was in the process of developing. – bjdesa Apr 28 '19 at 13:41
0

Thank you for your valuable insight and comments. I think the best approach was use

    void singlearrayd(double *temp1, int tempi, std::string str1)

Extending this idea and doing some more research using google I was able to extend this idea to handle 2D and 3D arrays.

    void doublearrayd(double *temp1, int tempi, int tempj, std::string str1){
        for (int md_j = 1; md_j<tempj; md_j++){
            for (int md_i = 1; md_i<tempi; md_i++){
                std::cout << *(temp1 + md_i*tempj + md_j) << "," << str1 << "(" << md_i << ";" << md_j << ")" << std::endl;
            }
        }
    }

    void triplearrayd(double *temp1, int tempi, int tempj, int tempk, std::string str1){
        for (int md_k = 1; md_k < tempk; md_k++){
            for (int md_j = 1; md_j<tempj; md_j++){
                for (int md_i = 1; md_i<tempi; md_i++){
                    std::cout << *(temp1 + md_i*tempj*tempk + md_j*tempk + md_k) << "," << str1 << "(" << md_i << ";" << md_j << ";" << md_k << ")" << std::endl;
                }
            }
        }
    }

https://en.wikipedia.org/wiki/Row-_and_column-major_order

How can I pass a dynamic multidimensional array to a function?

bjdesa
  • 63
  • 2
  • 9