0

I am struggling to replicate the following fortran 77 subroutine into C# method. The main problem is that I am trying to get rid of the fortran's goto statements.

The fortran to C# conversion looks fine, still the goto statements remained. Is there a way all of them can be replaced with conditionals or some other ways? Thank you.

Here is the fortran subroutine:

C PROGRAM TEMP
      subroutine TEMP (acl,adu,aeff,c,cair,cb,cbare, 
     + cclo,count1,csum,di,ed,emcl,emsk,enbal, 
     + enbal2,ere,erel,esw,eswdif,eswphy,eswpot, 
     + evap,facl,fcl,fec,feff,food,h,hc,he,ht,htcl,icl,j, 
     + mbody,p,po,r1,r2,rbare,rcl, 
     + rclo,rclo2,rdcl,rdsk,rob,rsum,sex,sigm,sw,swf,swm, 
     + ta,tbody,tcl,tcore,tmrt,tsk,v,vb,vb1,vb2, 
     + vpa,vpts,wetsk,wd,wr,ws,wsum,xx) 
      real acl,adu,aeff,c(0:10),cair,cb,cbare, 
     + cclo,csum,di,ed,emcl,emsk,enbal, 
     + enbal2,ere,erel,esw,eswdif,eswphy,eswpot, 
     + evap,facl,fcl,fec,feff,food,h,hc,he,ht,htcl,icl, 
     + mbody,p,po,r1,r2,rbare,rcl, 
     + rclo,rclo2,rdcl,rdsk,rob,rsum,sigm,sw,swf,swm, 
     + ta,tbody,tcl,tcore(1:7),tmrt,tsk,v,vb,vb1,vb2, 
     + vpa,vpts,wetsk,wd,wr,ws,wsum,xx 
      integer count1,count3,j,sex 
      wetsk = 0. 
      adu = 0.203 * mbody ** 0.425 * ht ** 0.725 
      hc = 2.67 + ( 6.5 * v ** 0.67) 
      hc = hc * (p /po) ** 0.55 
      feff = 0.725 
C 
      facl = (- 2.36 + 173.51 * icl - 100.76 * icl * icl + 19.28 
     + * (icl ** 3.)) / 100. 
C 
      if (facl .gt.1.) facl = 1. 
      rcl = (icl/6.45)/facl 
      if (icl.ge.2.) y = 1. 
      if ((icl .gt. 0.6) .and. (icl .lt. 2.)) y = (ht - 0.2) / ht 
      if ((icl .le. 0.6) .and. (icl .gt. 0.3)) y = 0.5 
      if ((icl .le. 0.3) .and. (icl .gt. 0.)) y = 0.1 
      r2 = adu * (fcl - 1. + facl) / (2. * 3.14 * ht * y) 
      r1 = facl * adu / (2. * 3.14 * ht * y) 
      di = r2 - r1 
C TEMPERATURE
      do 90 j = 1,7 
      tsk = 34. 
      count1 = 0 
      tcl = (ta + tmrt + tsk) / 3. 
      count3 = 1 
      enbal2 = 0. 
   20 acl = adu * facl + adu * (fcl - 1.) 
      rclo2 = emcl*sigm *((tcl+273.2)** 4.-(tmrt+273.2)** 4.)*feff 
      htcl = 6.28 * ht * y * di / (rcl * alog(r2/r1) * acl) 
      tsk = 1. / htcl * (hc * (tcl - ta) + rclo2) + tcl 
C RADIATION 
      aeff = adu * feff 
      rbare = aeff * (1.-facl) * emsk * sigm * 
     + ((tmrt + 273.2) ** 4. - (tsk + 273.2) ** 4.) 
      rclo = feff * acl * emcl * sigm * 
     + ((tmrt + 273.2) ** 4. - (tcl + 273.2) ** 4.) 
      rsum = rbare + rclo 
C CONVECT
      cbare = hc * (ta - tsk) * adu * (1. - facl) 
      cclo = hc * (ta - tcl ) * acl 
      csum = cbare + cclo 
C CORE
      c(0) = h + ere 
      c(1) = adu * rob * cb 
      c(2) = 18. - 0.5 * tsk 
      c(3) = 5.28 * adu * c(2) 
      c(4) = 0.0208 * c(1) 
      c(5) = 0.76075 * c(1) 
      c(6) = c(3) - c(5) - tsk * c(4) 
      c(7) = - c(0) * c(2) - tsk * c(3) + tsk * c(5) 
      c(8) = c(6) * c(6) - 4. * c(4) * c(7) 
      c(9) = 5.28 * adu - c(5) - c(4) * tsk 
      c(10) = c(9) * c(9) - 4. * c(4) * 
     + (c(5) * tsk - c(0) - 5.28 * adu * tsk) 
C 
      if (tsk.eq.36.) tsk=36.01 
      tcore(7) = c(0) / (5.28 * adu + c(1) * 6.3 / 3600.) + tsk 
      tcore(3) = c(0) / (5.28 * adu + (c(1) * 6.3 / 3600.) / 
     + (1 + 0.5 * (34. -tsk))) + tsk 
      if (c(10) .lt. 0.) goto 22 
      tcore(6) = (- c(9) - c(10) ** 0.5) / (2. * c(4)) 
      tcore(1) = (- c(9) + c(10) ** 0.5) / (2. * c(4)) 
   22 if (c(8) .lt. 0.) goto 24 
      tcore(2) = (- c(6) + abs(c(8)) ** 0.5) / (2. * c(4)) 
      tcore(5) = (- c(6) - abs(c(8)) ** 0.5) / (2. * c(4)) 
   24 tcore(4) = c(0) / (5.28 * adu + c(1) * 1. / 40.) + tsk 
C TRANSPARENCE
      tbody = 0.1 * tsk + 0.9 * tcore (j) 
      swm = 304.94 * (tbody - 36.6) * adu / 3600000. 
      vpts = 6.11 * 10. ** (7.45 * tsk / (235. + tsk)) 
      if (tbody .le. 36.6) swm = 0. 
      swf = 0.7 * swm 
      if(sex .eq. 1) sw = swm 
      if(sex .eq. 2) sw = swf 
      eswphy = - sw * evap 
      he = 0.633 * hc / (p * cair) 
      fec = 1. / (1. + 0.92 * hc * rcl) 
      eswpot = he * (vpa - vpts) * adu * evap * fec 
      wetsk = eswphy / eswpot 
      if (wetsk .gt. 1.) wetsk = 1. 
      eswdif = eswphy - eswpot 
      if (eswdif .le. 0.) esw = eswpot 
      if (eswdif .gt. 0.) esw = eswphy 
      if (esw .gt. 0.) esw = 0. 
C DIFFERENCE
      rdsk = 0.79 * 10. ** 7. 
      rdcl = 0. 
      ed = evap / (rdsk + rdcl) * adu * (1 - wetsk) * (vpa-vpts) 
C VB 
      vb1 = 34. - tsk 
      vb2 = tcore(j) - 36.6 
      if (vb2 .lt.0.) vb2 = 0. 
      if (vb1 .lt.0.) vb1 = 0. 
      vb = (6.3 + 75. * (vb2)) / (1. + 0.5 * vb1) 
C BALANCE
      enbal = h + ed + ere + esw + csum + rsum + food 
C COVER
      if (count1 .eq.0) xx = 1. 
      if (count1 .eq.1) xx = 0.1 
      if (count1 .eq.2) xx = 0.01 
      if (count1 .eq.3) xx = 0.001 
      if (enbal .gt. 0.) tcl = tcl + xx 
      if (enbal .lt. 0.) tcl = tcl - xx 
      if ((enbal .le. 0.) .and. (enbal2 .gt. 0.)) goto 30 
      if ((enbal .ge. 0.) .and. (enbal2 .lt. 0.)) goto 30 
      enbal2 = enbal 
      count3 = count3 + 1 
C 
      if (count3 .gt. 200) goto 30 
      goto 20 
   30 if ((count1 .eq.0.).or.(count1.eq.1.).or.(count1.eq.2.)) then 
         count1 = count1 + 1. 
         enbal2 = 0. 
         goto 20 
      end if 
C 
      if (count1 .eq. 3.) then 
C 
         if ((j .eq. 2) .or. (j .eq. 5)) goto 40 
         if ((j .eq. 6) .or. (j .eq. 1)) goto 50 
         if (j .eq. 3) goto 60 
         if (j .eq. 7) goto 70 
         if (j .eq. 4) goto 80 
      end if 
   40 if (c(8) .lt. 0.) goto 90 
      if ((tcore(j) .ge. 36.6) .and. (tsk .le. 34.050)) goto 80 
      goto 90 
   50 if (c(10) .lt. 0. ) goto 90 
      if ((tcore(j) .ge. 36.6) .and. (tsk .gt. 33.850)) goto 80 
      goto 90 
   60 if ((tcore(j) .lt. 36.6) .and. (tsk .le. 34.000)) goto 80 
      goto 90 
   70 if ((tcore(j) .lt. 36.6) .and. (tsk .gt. 34.000)) goto 80 
      goto 90 
   80 if ((j .ne. 4) .and. (vb .ge. 91.)) goto 90 
      if ((j. eq. 4) .and. (vb .lt. 89.)) goto 90 
      if (vb .gt. 90.) vb = 90. 
C LOSSES
      ws = sw * 3600. * 1000. 
      if (ws .gt.2000.) ws = 2000. 
      wd = ed / evap * 3600. * (-1000.) 
      wr = erel / evap * 3600. * (-1000.) 
      wsum = ws + wr + wd 
      goto 100 
   90 continue 
  100 return 
      end 

And here is the C# attempt to replicate the upper fortran subroutine:

using System;

public static class GlobalMembers_TEMP
{

//C PROGRAM TEMP
// --------------------------------------------
    public static void TEMP(acl, adu, aeff, c, cair, cb, cbare, cclo, count1, csum, di, ed , emcl , emsk , enbal , enbal2 , ere , erel , esw , eswdif , eswphy , eswpot , evap , facl , fcl , fec , feff , food , h , hc , he , ht , htcl , icl , j , mbody , p , po , r1 , r2 , rbare , rcl , rclo , rclo2 , rdcl , rdsk , rob , rsum , sex , sigm , sw , swf , swm , ta , tbody , tcl, tcore , tmrt , tsk , v , vb , vb1 , vb2 , vpa , vpts , wetsk , wd , wr , ws , wsum , xx)
    {
        float acl;
        float adu;
        float aeff;
        float[] c = new float[11];  // ???
        float cair;
        float cb;
        float cbare;
        float cclo;
        float csum;
        float di;
        float ed;
        float emcl;
        float emsk;
        float enbal;
        float enbal2;
        float ere;
        float erel;
        float esw;
        float eswdif;
        float eswphy;
        float eswpot;
        float evap;
        float facl;
        float fcl;
        float fec;
        float feff;
        float food;
        float h;
        float hc;
        float he;
        float ht;
        float htcl;
        float icl;
        float mbody;
        float p;
        float po;
        float r1;
        float r2;
        float rbare;
        float rcl;
        float rclo;
        float rclo2;
        float rdcl;
        float rdsk;
        float rob;
        float rsum;
        float sigm;
        float sw;
        float swf;
        float swm;
        float ta;
        float tbody;
        float tcl;
        float[] tcore = new float[7];   // ???
        float tmrt;
        float tsk;
        float v;
        float vb;
        float vb1;
        float vb2;
        float vpa;
        float vpts;
        float wetsk;
        float wd;
        float wr;
        float ws;
        float wsum;
        float xx;
        int count1;
        int count3;
        int j;
        int sex;
        wetsk = 0.0;
        adu = 0.203 * mbody * *0.425 * ht * *0.725;
        hc = 2.67 + (6.5 * v * *0.67);
        hc = hc * (p / po) * *0.55;
        feff = 0.725;
        //C rcl = icl / 6.45
        facl = (-2.36 + 173.51 * icl - 100.76 * icl * icl + 19.28 * (icl * *3.0)) / 100.0;
        //C
        if (facl > 1.0)
        {
            facl = 1.0;
        }
        rcl = (icl / 6.45) / facl;
        if (icl >= 2.0)
        {
            y = 1.0;
        }
        if ((icl > 0.6)) && ((icl < 2.0)) 
        {
            y = (ht - 0.2) / ht;
        }
        if ((icl <= 0.6)) && ((icl > 0.3)) 
        {
            y = 0.5;
        }
        if ((icl <= 0.3)) && ((icl > 0.0)) 
        {
            y = 0.1;
        }
        r2 = adu * (fcl - 1.0 + facl) / (2.0 * 3.14 * ht * y);
        r1 = facl * adu / (2.0 * 3.14 * ht * y);
        di = r2 - r1;
        //C TEMPERATURE
        for (90 j = 1, 7)
        {
            tsk = 34.0;
            count1 = 0;
            tcl = (ta + tmrt + tsk) / 3.0;
            count3 = 1;
            enbal2 = 0.0;
    g20:
            acl = adu * facl + adu * (fcl - 1.0);
            rclo2 = emcl * sigm * ((tcl + 273.2) * *4.0 - (tmrt + 273.2) * *4.0) * feff;
            htcl = 6.28 * ht * y * di / (rcl * alog(r2 / r1) * acl);
            tsk = 1.0 / htcl * (hc * (tcl - ta) + rclo2) + tcl;
            //C RADIATION
            aeff = adu * feff;
            rbare = aeff * (1.0 - facl) * emsk * sigm * ((tmrt + 273.2) * *4.0 - (tsk + 273.2) * *4.0);
            rclo = feff * acl * emcl * sigm * ((tmrt + 273.2) * *4.0 - (tcl + 273.2) * *4.0);
            rsum = rbare + rclo;
            //C CONVECT
            cbare = hc * (ta - tsk) * adu * (1.0 - facl);
            cclo = hc * (ta - tcl) * acl;
            csum = cbare + cclo;
            //C CORE
            c[0] = h + ere;
            c[1] = adu * rob * cb;
            c[2] = 18.0 - 0.5 * tsk;
            c[3] = 5.28 * adu * c[2];
            c[4] = 0.0208 * c[1];
            c[5] = 0.76075 * c[1];
            c[6] = c[3] - c[5] - tsk * c[4];
            c[7] = -c[0] * c[2] - tsk * c[3] + tsk * c[5];
            c[8] = c[6] * c[6] - 4.0 * c[4] * c[7];
            c[9] = 5.28 * adu - c[5] - c[4] * tsk;
            c[10] = c[9] * c[9] - 4.0 * c[4] * (c[5] * tsk - c[0] - 5.28 * adu * tsk);
            //C
            if (tsk == 36.0)
            {
                tsk = 36.01;
            }
            tcore[7] = c[0] / (5.28 * adu + c[1] * 6.3 / 3600.0) + tsk;
            tcore[3] = c[0] / (5.28 * adu + (c[1] * 6.3 / 3600.0) / (1 + 0.5 * (34.0 - tsk))) + tsk;
            if (c[10] < 0.0)
            {
                goto g22;
            }
            tcore[6] = (-c[9] - c[10] * *0.5) / (2.0 * c[4]);
            tcore[1] = (-c[9] + c[10] * *0.5) / (2.0 * c[4]);
    g22:
            if (c[8] < 0.0)
            {
                goto g24;
            }
            tcore[2] = (-c[6] + Math.Abs(c[8]) * *0.5) / (2.0 * c[4]);
            tcore[5] = (-c[6] - Math.Abs(c[8]) * *0.5) / (2.0 * c[4]);
    g24:
            tcore[4] = c[0] / (5.28 * adu + c[1] * 1.0 / 40.0) + tsk;
            //C TRANSPARENCE
            tbody = 0.1 * tsk + 0.9 * tcore[j];
            swm = 304.94 * (tbody - 36.6) * adu / 3600000.0;
            vpts = 6.11 * 10.0 * *(7.45 * tsk / (235.0 + tsk));
            if (tbody <= 36.6)
            {
                swm = 0.0;
            }
            swf = 0.7 * swm;
            if (sex == 1)
            {
                sw = swm;
            }
            if (sex == 2)
            {
                sw = swf;
            }
            eswphy = -sw * evap;
            he = 0.633 * hc / (p * cair);
            fec = 1.0 / (1.0 + 0.92 * hc * rcl);
            eswpot = he * (vpa - vpts) * adu * evap * fec;
            wetsk = eswphy / eswpot;
            if (wetsk > 1.0)
            {
                wetsk = 1.0;
            }
            eswdif = eswphy - eswpot;
            if (eswdif <= 0.0)
            {
                esw = eswpot;
            }
            if (eswdif > 0.0)
            {
                esw = eswphy;
            }
            if (esw > 0.0)
            {
                esw = 0.0;
            }
            //C DIFFERENCE
            rdsk = 0.79 * 10.0 * *7.0;
            rdcl = 0.0;
            ed = evap / (rdsk + rdcl) * adu * (1 - wetsk) * (vpa - vpts);
            //C VB
            vb1 = 34.0 - tsk;
            vb2 = tcore[j] - 36.6;
            if (vb2 < 0.0)
            {
                vb2 = 0.0;
            }
            if (vb1 < 0.0)
            {
                vb1 = 0.0;
            }
            vb = (6.3 + 75.0 * (vb2)) / (1.0 + 0.5 * vb1);
            //C BALANCE
            enbal = h + ed + ere + esw + csum + rsum + food;
            //C COVER
            if (count1 == 0)
            {
                xx = 1.0;
            }
            if (count1 == 1)
            {
                xx = 0.1;
            }
            if (count1 == 2)
            {
                xx = 0.01;
            }
            if (count1 == 3)
            {
                xx = 0.001;
            }
            if (enbal > 0.0)
            {
                tcl = tcl + xx;
            }
            if (enbal < 0.0)
            {
                tcl = tcl - xx;
            }
            if ((enbal <= 0.0)) && ((enbal2 > 0.0)) 
            {
                goto g30;
            }
            if ((enbal >= 0.0)) && ((enbal2 < 0.0)) 
            {
                goto g30;
            }
            enbal2 = enbal;
            count3 = count3 + 1;
            //C
            if (count3 > 200)
            {
                goto g30;
            }
            goto g20;
    g30:
            if ((count1 == 0.0))
            {
                || ((count1 == 1.0)) || ((count1 == 2.0))
                {
                count1 = count1 + 1.0;
                enbal2 = 0.0;
                goto g20;
                }
            }
            //C
            if (count1 == 3.0)
            {
                //C
                if ((j == 2))
                {
                    || ((j == 5)) goto g40;
                }
                if ((j == 6))
                {
                    || ((j == 1)) goto g50;
                }
                if (j == 3)
                {
                    goto g60;
                }
                if (j == 7)
                {
                    goto g70;
                }
                if (j == 4)
                {
                    goto g80;
                }
            }
    g40:
            if (c[8] < 0.0)
            {
                goto g90;
            }
            if ((tcore[j] >= 36.6)) && ((tsk <= 34.050))
            {
                goto g80;
            }
            goto g90;
    g50:
            if (c[10] < 0.0)
            {
                goto g90;
            }
            if ((tcore[j] >= 36.6)) && ((tsk > 33.850))
            {
                goto g80;
            }
            goto g90;
    g60:
            if ((tcore[j] < 36.6)) && ((tsk <= 34.000))
            {
                goto g80;
            }
            goto g90;
    g70:
            if ((tcore[j] < 36.6)) && ((tsk > 34.000))
            {
                goto g80;   
            }
            goto g90;
    g80:
            if ((j != 4)) && ((vb >= 91.0)) 
            {
                goto g90;
            }
            if ((j.eq.4)) && ((vb < 89.0))
            {
                 goto g90;
            }
            if (vb > 90.0)
            {
                vb = 90.0;
            }
            //C LOSSES
            ws = sw * 3600.0 * 1000.0;
            if (ws > 2000.0)
            {
                ws = 2000.0;
            }
            wd = ed / evap * 3600.0 * (-1000.0);
            wr = erel / evap * 3600.0 * (-1000.0);
            wsum = ws + wr + wd;
            goto g100;

        }
    g100:
        return;
    }
}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
marco
  • 899
  • 5
  • 13
  • 21
  • 1
    `&& ((icl > 0.3)) y = 0.5;` does that even compile? And yes, many of the gotos can be substituted with other constructs. – Sami Kuhmonen Jul 05 '15 at 14:12
  • 1
    In the worst case you may try to turn the loop body into some kind of state machine, where each state represents a label. Just be careful with loop iterator value. – Eugene Podskal Jul 05 '15 at 14:28
  • Thank you for the replies. @Sanu Kuhmonen. I edited all ```&&``` logical operators. What would you recommend for substitution of ```goto``` statements? @Eugene Podskal: The problem is that, I do not code in c# (and I am not a programmer). I code in python though. What I will try to do is to use varycode.com to convert the upper C# to python. That is my initial desire (to replicate the fortran code to python). Would you be a bit more explicit in the "state machine" method? Thank you. – marco Jul 05 '15 at 14:31
  • @marco I will add some explanation some. Unfortunately it still won't look very nice. – Eugene Podskal Jul 05 '15 at 14:36
  • GOTO is a valid construct. If you understand the logic, it is usually easy to remove GOTOs. If you don't understand the logic, leave at as is. In the structured programming revolution days people assumed that all GOTO statements are evil but this is not the case see for example Knuths: "Structured Programming with GOTO Statement" at: http://c2.com/cgi/wiki?StructuredProgrammingWithGoToStatements – NoChance Jul 05 '15 at 14:43
  • @Eugene Podskal It does not matter if it will look nice or not. I would be very grateful on that explanation. Thank you in advance. @ Emmad Kareem: I am actually going to convert this C# code to Python. There's no ```goto``` in python. – marco Jul 05 '15 at 14:46
  • Then you got to understand the logic. – NoChance Jul 05 '15 at 14:47
  • I'm not sure how anyone here could help to make this code any better without some understanding of what the routine is meant to accomplish. And in the state the code is in, there is no indication of the problem domain at all from variable names or function signatures. By the time anyone worked out what the function was, much less how to replace the GOTO statements, a complete replacement could be written from scratch. – Claies Jul 05 '15 at 14:57
  • I would suggest simplest form of removing goto, Write C# code with Goto, compile, decompile code with DotPeak, it will automatically create loops as Decompiler will analyze `loop`, `if` etc constructs and it will create appropriate statement. – Akash Kava Jul 05 '15 at 16:16

2 Answers2

2

You can try to turn this code into a state machine where each state represents a single goto inside the loop.

For that you will need to turn your method into a class, that contains all the parameters and variables as class fields, and has one constructor that initializes appropriate fields and (optionally) runs the "method".

I won't transition your method because it is far too huge as an example, but I will give you the following:

Original not converted method:

public static void Do(Int32 param1, Int32 param2)
{            
    Int32 loopI;
    Int32 outBefore, 
        outAfter,
        outGoto1 = 0,
        outGoto2 = 0;
    Int32[] outValues = new Int32[7];

    Console.WriteLine("METHOD:");

    Console.WriteLine("DoBeforeLoop");
    outBefore = param1 + param2;

    Boolean wasInGoto2 = false;

    for (loopI = 0; loopI < 7; loopI++)
    {
        Console.WriteLine("Iteration {0}", loopI);

        Console.WriteLine("FortranLoopMethodState.LoopCycleStart");
        outValues[loopI] = loopI;

    goto1:
        Console.WriteLine("FortranLoopMethodState.Goto1");
        outGoto1 = loopI + param1;

        if (wasInGoto2)
        {
            wasInGoto2 = false;
            goto end;
        }

        wasInGoto2 = true;
        Console.WriteLine("FortranLoopMethodState.Goto2");
        outGoto2 = loopI + param2;
        goto goto1;
    end:
        DoNothing(); // We don't use break, do we?
    }

    Console.WriteLine("DoAfterLoop");
    outAfter = param1 - param2;

    Console.WriteLine(outGoto1);
    Console.WriteLine(outGoto2);
}

Converted method:

public class ExecuteFortranMethod
{
    private enum FortranLoopMethodState
    {
        LoopCycleStart,
        Goto1,
        Goto2,
        LoopCycleEnded
    }

    #region Params

    private Int32 param1;
    private Int32 param2;

    #endregion


    #region Variables

    public Int32 loopI;
    public Int32 outBefore, 
        outAfter,
        outGoto1,
        outGoto2;
    public Int32[] outValues = new Int32[7];

    #endregion


    #region Constructors

    public ExecuteFortranMethod(Int32 param1, Int32 param2)
    {
        this.param1 = param1;
        this.param2 = param2;

        this.Invoke();
    }

    #endregion


    #region Methods

    private void Invoke()
    {
        Console.WriteLine("STATE MACHINE CLASS:");

        this.DoBeforeLoop();

        for (this.loopI = 0; this.loopI < 7; this.loopI++)
        {
            Console.WriteLine("Iteration {0}", this.loopI);

            var state = FortranLoopMethodState.LoopCycleStart;
            do
            {
                state = DoLoop(state);
            }
            while (state != FortranLoopMethodState.LoopCycleEnded);
        }

        this.DoAfterLoop();
    }

    #endregion


    #region "Method" body methods

    private void DoBeforeLoop()
    {
        Console.WriteLine("DoBeforeLoop");
        this.outBefore = this.param1 + this.param2;
    }

    private void DoAfterLoop()
    {
        Console.WriteLine("DoAfterLoop");
        this.outAfter = this.param1 - this.param2;
    }

    Boolean wasInGoto2;

    private FortranLoopMethodState DoLoop(FortranLoopMethodState state)
    {
        switch (state)
        {
            case FortranLoopMethodState.LoopCycleStart:
                {
                    Console.WriteLine("FortranLoopMethodState.LoopCycleStart");
                    this.outValues[this.loopI] = this.loopI;
                    return FortranLoopMethodState.Goto1;
                }
            case FortranLoopMethodState.Goto1:
                {
                    Console.WriteLine("FortranLoopMethodState.Goto1");
                    this.outGoto1 = this.loopI + this.param1;

                    if (this.wasInGoto2)
                    {
                        this.wasInGoto2 = false;
                        return FortranLoopMethodState.LoopCycleEnded;
                    }
                    return FortranLoopMethodState.Goto2;
                }
            case FortranLoopMethodState.Goto2:
                {
                    this.wasInGoto2 = true;
                    Console.WriteLine("FortranLoopMethodState.Goto2");
                    this.outGoto2 = this.loopI + this.param2;
                    return FortranLoopMethodState.Goto1;
                }
            default:
                throw new InvalidOperationException("The state is invalid");
        }
    }

    #endregion
}   

Program.cs

Do(10, 20);
Console.WriteLine();

var result = new ExecuteFortranMethod(10, 20);

Console.WriteLine(result.outGoto1);
Console.WriteLine(result.outGoto2);

Console.WriteLine("Press any key");
Console.ReadKey(true);    

As you can see the outputs are equivalent. I won't describe exactly how you can transfer the actual method, because it is a tedious (but not unreal) task with no real shortcuts and most of the ideas will be clear when you read even this tiny example.

P.S.1: While there are no real shortucts (except of course of writing some small tool that can generate such state machine classes for the given code), you may simplify the manual job with a bit of regular expressions search and replace (Ctrl+F). For example, replacing pattern goto(\d+) with case State.Goto$1 { will replace all gotos with switch case stubs.

P.S.2: If there are far too many switch cases(gotos) you may want to replace switch with some Dictionary lookup.

P.S.3: And as many have already pointed it, I'd still recommend you to try to understand the original code flow and to rewrite it from scratch.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • _And as many have already pointed it, I'd still recommend you to try to understand the original code flow and to rewrite it from scratch_ Bold that one!! – TaW Jul 05 '15 at 15:33
  • Thank you TaW. I actually did try to do that, but in Python. And I failed. I replaced the ```goto``` with a number of ```continue```, additional loop and ```break``` statements, and the result is not equal to compiled fortran version. This is why I tried going the other way around: trying to use the convertors to get the code converted from fortran to C#. Then once the ```goto``` statements are removed from the C# code, I would again convert it from C# to python. – marco Jul 05 '15 at 16:02
  • Thank you Eugene for the massive explanation. It looks like I would have to learn the c# first in order to understand your reply. – marco Jul 06 '15 at 12:29
  • @marco There is no construct in this reply that doesn't map directly to a Pythonic one, so if you know Python it can be relatively easily translated. Unfortunately, it is not so easy to similarly move your original method - but still it is just a matter of patience and attention. And if you don't like this state machine approach to the problem you may try to adapt the [guest's proposal](http://stackoverflow.com/a/31232126/3745022), but because of non-linearity of these jumps, it won't be much easier. – Eugene Podskal Jul 06 '15 at 18:52
  • @EugenePodskal The actual fortran script is a bit larger, this is only one subroutine. I managed to replicate this other part to python, but I haven't succeeded in replicating this one. Only because of ```goto``` statements in this one. I have all the patience, but I still do not understand your code. For example, I am not sure the ```switch``` statement (case) exist in python. I think I would have to understand the whole c# syntax first, to be able to replicate your example. – marco Jul 06 '15 at 19:11
  • @marco Thought that Python has one, sorry. But I have already mentioned dictionary as a viable alternative to switch when you have a lot of cases. And for Python it seems to be the only way -http://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python – Eugene Podskal Jul 07 '15 at 07:21
  • @EugenePodskal will try the dictionary method, thank you. Still I am having difficulties in understanding your c# code. – marco Jul 07 '15 at 18:12
1

A goto inside the loop means two things:

  • stop the current execution of the loop (that's like using continue)
  • skip everything before the goto label (that's like using an if)

Instead of if-statements you could theoretically use a big switch statement without breaks on each case (fall through mode), so instead of doing a goto you can just set the switch value (create a variable) to the correct position and then call continue.

But that would be the same logic and lead to the same problems that you have when using gotos. It is not understandable, debuggable, maintainable for someone who didn't write the code. After 3-4 months it is even hard for the same person.

As a first step you should try to take each block and identify what exactly it does and if possible extract it to a method/function with a good name.

When your finished you hopefully have a huge goto mess with some function calls in it. Try to reduce the mess with if statements. g70 looks like a probable candidate because it conditionaly performs the g80 stuff or skips it and continues at g90, but haven't looked in detail at it.

Once you have it down to that level, you can also make execution graphs, one row (maybe in a spreadsheet) for each goto label and just write down which functions then get called until it reaches another goto.

Basic for a good refactoring is a good understanding of the problem, so maybe interviewing the person who wrote the pascal code to explain what it does can help you a lot.

Would be fun to see what you made out of the code once it's done and what it really does!

guest
  • 11
  • 1
  • Just tried to skip through the code and the only thing I understood is that females sweat 30% less than males? One thing you can do is give the variables proper names, your C# IDE should have a nice refactoring tool, just click on a variable and select refactor->rename. – guest Jul 05 '15 at 15:56
  • Maybe the IDE has also an extract method tool and an remove goto tool :) worth looking for one at least ... – guest Jul 05 '15 at 15:57
  • Hi @guest. I am using Notepad++, and it does not have remove goto tool. I have just installed Visual C# 2010 Express, and googled for some time. I can not find the goto replacement method. I also respect your time and reply, but do not understand how to implement it, as python does not have the ```switch``` method. I tried is to insert another for loop at "g20".Then I used python's ```break``` statement at g70,but also g40,g50,g60. In this way, if the condition is fulfilled, the code continues to g80.If it is not, it breaks the for loop at g20,and returns to the 90 loop.It doesn't work though. – marco Jul 06 '15 at 19:27