Sauberer Code trotz goto ???



  • Ich benutze goto's manchmal wie sie in dem folgenden Pseudocode angegeben sind:

    void DoSomething(unsigned int a)
    {
      // Reserviere Speicher, öffne Handles, ....
      int m = new int[100];
      double n = new double[100];
      ...
      // Mach was
      if (a > 5)
      {
        if (!MyFunction(m, n, a))
        {
           printf("Error 100\n");
           goto FunctionEnd;
        }
        for (unsigned int i = 0; i < a; i++)
        {
          if (!MyFunction2(i))
            goto FunctionEnd;
          if (a*a > 100)
          {
             if (!MyFunction3(m, i))
             {
               printf("Error 100\n");
               goto FunctionEnd;
             }
             else if (a * a > 1000)
               goto FunctionEnd;
          }
        }
        ...
      }
      // Gebe Resourcen frei, schliesse Handles, ...
       FunctionEnd:
      /***********/
      delete[] m;
      delete[] n;
      ...
    }
    

    Frage: Gibt es eine Möglichkeit das Ganze in einem sauberen Stil zu schreiben ? 😕



  • Lösung = if + bool

    Sauberer Code trotz goto geht nicht.



  • void DoSomething(unsigned int a)
    {
      // Reserviere Speicher, öffne Handles, ....
      int m = new int[100];
      double n = new double[100];
      ...
      // Mach was
      if (a > 5)
      {
        if (!MyFunction(m, n, a))
        {
           printf("Error 100\n");       
        }
        else
        {
         for (unsigned int i = 0; i < a; i++)
         {
          if (!MyFunction2(i))
            break;
          if (a*a > 100)
          {
             if (!MyFunction3(m, i))
             {
               printf("Error 100\n");
               break;
             }
             else if (a * a > 1000)
               break;
          }
         }
        }
        ...
      }
      // Gebe Resourcen frei, schliesse Handles, ...
      /***********/
      delete[] m;
      delete[] n;
      ...
    }
    

    Ansonsten mit Abbruchbedingungen und continue arbeiten.



  • RAII:

    void DoSomething(unsigned int a)
    {
      // Reserviere Speicher, öffne Handles, ....
      std::tr1::array<int, 100> m;
      std::tr1::array<double, 100> n;
      ...
      // Mach was
      if (a > 5)
      {
        if (!MyFunction(m, n, a))
        {
           printf("Error 100\n");
           return;
        }
        for (unsigned int i = 0; i < a; i++)
        {
          if (!MyFunction2(i))
            return;
          if (a*a > 100)
          {
             if (!MyFunction3(m, i))
             {
               printf("Error 100\n");
               return;
             }
             else if (a * a > 1000)
               return;
          }
        }
        ...
      }
    }
    

  • Mod

    Nimm statische Arrays oder Containerklassen, dann sparst du dir das delete und beim Verlassen der Funktion (egal von wo) wird alles abgeräumt.

    Die dynamische Speicheranforderung ist in deinem Fall besonders rätselhaft, da du statische Arrays dynamisch reservierst.



  • @Fellhuhn
    Das Problem hinter deiner Lösung ist dass ich pro goto potenziell eine Verschachtelungstiefe gewinne. Das macht die Sache schnell auch unüberischtlich.

    @Seppj und life
    Ich mache da mehr als nur Speicher zu reservieren. Und zwar benutze ich in der Funktion mehrere tempöräre Dateien, welche im am Ende der Funktion löschen möchte. Ich habe vier WINAPI-Handles offen (zwei Registry Handles, ein Mutex Handle und ein COM Port Handle), welche ich am Ende schliessen möchte, ...
    Das was ich da im Code hingeschrieben habe war nur ein Beispiel. Ich reserviere nur dann Speicher in der folgenden Art wenn ich keinen sinnvollen Container finde und ich die Größe des Arrays erst zur Laufzeit weis.

    Alternative:

    void DoSomething(unsigned int a)
    {
      // Reserviere Speicher, öffne Handles, ....
      int m = new int[100];       // int* m = new int[100];
      double n = new double[100]; // double* n = new double[100];
      ...
      try
      {
        // Mach was
        if (a > 5)
        { 
          if (!MyFunction(m, n, a))
          {
             printf("Error 100\n");
             throw MyException();
          }
          for (unsigned int i = 0; i < a; i++)
          {
            if (!MyFunction2(i))
              throw MyException();
            if (a*a > 100)
            {
              if (!MyFunction3(m, i))
              {
                printf("Error 100\n");
                throw MyException();
              }
              else if (a * a > 1000)
                throw MyException();
            } 
          }
          ...
        }
      }
      catch (MyException& e)
      {
        // Gebe Resourcen frei, schliesse Handles, ...
        /***********/
        delete[] m;
        delete[] n;
      }
      ...
    }
    


  • Ich habe vier WINAPI-Handles offen (zwei Registry Handles, ein Mutex Handle und ein COM Port Handle), welche ich am Ende schliessen möchte

    Dein Code mit goto ist nicht sicher gegenueber Exceptions. Mittels RAI hast du dieses Problem auch geloest.

    ich die Größe des Arrays erst zur Laufzeit weis

    std::vector



  • void DoSomething(unsigned int a)
    {
      // Reserviere Speicher, öffne Handles, ....
      int m = new int[100];       // int* m = new int[100];
      double n = new double[100]; // double* n = new double[100];
      ...
      try
      {
        // Mach was
        if (a > 5)
        {
          if (!MyFunction(m, n, a))
          {
             printf("Error 100\n");
             return;
          }
          for (unsigned int i = 0; i < a; i++)
          {
            if (!MyFunction2(i))
              return;
            if (a*a > 100)
            {
              if (!MyFunction3(m, i))
              {
                printf("Error 100\n");
                return;
              }
              else if (a * a > 1000)
                return;
            }
          }
          ...
        }
      }
      __finally
      {
        // Gebe Resourcen frei, schliesse Handles, ...
        /***********/
        delete[] m;
        delete[] n;
      }
      ...
    }
    

    😃



  • __finally
    

    Gibt es nicht in C++.



  • Bitte ein Bit schrieb:

    ... Gebe ...
    

    *syntax error* :p

    Gruß,

    Simon2.



  • Bitte ein Bit schrieb:

    @Seppj und life
    Ich mache da mehr als nur Speicher zu reservieren. Und zwar benutze ich in der Funktion mehrere tempöräre Dateien, welche im am Ende der Funktion löschen möchte. Ich habe vier WINAPI-Handles offen (zwei Registry Handles, ein Mutex Handle und ein COM Port Handle), welche ich am Ende schliessen möchte, ...

    Wie gesagt: RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization).



  • Danke für die Info's zu RAII. Das war genau das was ich gesucht habe. 👍



  • #define END_MY_FUNCTION delete[] m; \
      delete[] n; \
      return
    
    void DoSomething(unsigned int a) 
    { 
      // Reserviere Speicher, öffne Handles, .... 
      int m = new int[100]; 
      double n = new double[100]; 
      ... 
      // Mach was 
      if (a > 5) 
      { 
        if (!MyFunction(m, n, a)) 
        { 
           printf("Error 100\n"); 
           END_MY_FUNCTION;
        } 
        for (unsigned int i = 0; i < a; i++) 
        { 
          if (!MyFunction2(i)) 
            END_MY_FUNCTION;
          if (a*a > 100) 
          { 
             if (!MyFunction3(m, i)) 
             { 
               printf("Error 100\n"); 
               END_MY_FUNCTION;
             } 
             else if (a * a > 1000) 
               END_MY_FUNCTION;
          } 
        } 
        ... 
      } 
      END_MY_FUNCTION;
      ... 
    }
    


  • asdfasd schrieb:

    #define END_MY_FUNCTION delete[] m; \
      delete[] n; \
      return
    
    void DoSomething(unsigned int a) 
    { 
      // Reserviere Speicher, öffne Handles, .... 
      int m = new int[100]; 
      double n = new double[100]; 
      ... 
      // Mach was 
      if (a > 5) 
      { 
        if (!MyFunction(m, n, a)) 
        { 
           printf("Error 100\n"); 
           END_MY_FUNCTION;
        } 
        for (unsigned int i = 0; i < a; i++) 
        { 
          if (!MyFunction2(i)) 
            END_MY_FUNCTION;
          if (a*a > 100) 
          { 
             if (!MyFunction3(m, i)) 
             { 
               printf("Error 100\n"); 
               END_MY_FUNCTION;
             } 
             else if (a * a > 1000) 
               END_MY_FUNCTION;
          } 
        } 
        ... 
      } 
      END_MY_FUNCTION;
      ... 
    }
    

    Davon abgesehen das es eine scheiss Lösung, ist sie auch noch fehlerhaft.



  • Bitte ein Bit schrieb:

    Ich habe vier WINAPI-Handles offen (zwei Registry Handles, ein Mutex Handle und ein COM Port Handle), welche ich am Ende schliessen möchte, ...

    Für Registry- und Mutex-Handles gibt es fertige RAII-Lösungen:
    CRegKey: http://msdn.microsoft.com/en-us/library/xka57xy4(VS.80).aspx
    CMutex: http://msdn.microsoft.com/en-us/library/tt45160e(v=VS.71).aspx
    Falls du mit dem Com Port über die normalen File-Funktionen arbeitest (Gibt es überhaupt andere Möglichkeiten?) gibt es dafür auch CFile: http://msdn.microsoft.com/en-us/library/60fh2b6f(VS.80).aspx

    Die Klassen kümmern sich jeweils im Destruktor selbst um das Schließen der Handles.


  • Administrator

    rean schrieb:

    Bitte ein Bit schrieb:

    Ich habe vier WINAPI-Handles offen (zwei Registry Handles, ein Mutex Handle und ein COM Port Handle), welche ich am Ende schliessen möchte, ...

    Für Registry- und Mutex-Handles gibt es fertige RAII-Lösungen:
    CRegKey: http://msdn.microsoft.com/en-us/library/xka57xy4(VS.80).aspx
    CMutex: http://msdn.microsoft.com/en-us/library/tt45160e(v=VS.71).aspx
    Falls du mit dem Com Port über die normalen File-Funktionen arbeitest (Gibt es überhaupt andere Möglichkeiten?) gibt es dafür auch CFile: http://msdn.microsoft.com/en-us/library/60fh2b6f(VS.80).aspx

    Die Klassen kümmern sich jeweils im Destruktor selbst um das Schließen der Handles.

    Das sind aber ATL/MFC Klassen. Die bekommt man nicht gratis, dazu muss man sich eine Visual Studio Version Standard oder besser kaufen. Sowas kann man sich aber relativ leicht auch selber nachbauen, auch wenn es am Ende nur ein simpler Guard ohne Funktionen ist. Allerdings denke ich, dass es im Internet bereits fertige Klassen gäbe, wobei ich keine direkt kenne.

    Grüssli



  • @rean
    Cool. Ich kenne die Klassen gar nicht.

    @asdfasd
    Deine Lösung hat den massiven Nachteil dass das Debugging zur Compilezeit und Laufzeit schwer wird. Bau mal ein Fehler in dein Makro ein und versuche mal zu debuggen. :p

    Nichtsdestotrotz habe ich aber deine Lösung auch schon in fremden Projekten entdeckt.



  • Bitte ein Bit schrieb:

    Deine Lösung hat den massiven Nachteil dass das Debugging zur Compilezeit und Laufzeit schwer wird. Bau mal ein Fehler in dein Makro ein und versuche mal zu debuggen. :p

    Da sind schon zwei Fehler drin. Makros die mehrere Befehle einfügen aber nicht umklammert sind, sorgen schnell für Probleme, wie auch bei seinem Beispiel.



  • Janjan schrieb:

    Lösung = if + bool
    Sauberer Code trotz goto geht nicht.

    Ganz schlecht.



  • volkard schrieb:

    Janjan schrieb:

    Lösung = if + bool
    Sauberer Code trotz goto geht nicht.

    Ganz schlecht.

    Aber sowas von nicht schlecht du goto-Liebhaber!


Log in to reply