[BC++] Dritte Wurzel aus einer negativen Zahl (pow domain error) ziehen

Sicherlich ist es schon vielen so ergangen: Excel rechnet es, der Taschenrechner auch, bloß im C++-Code ist der Wurm drin. Beim Ziehen der dritten Wurzel aus einer negativen Zahl mittels pow-Funktion (x^1/3) poppt ein pow domain error auf:

#include <math.h>

double x = -27.0;
double y = pow(x, (1.0 / 3.0)); // <-- pow domain error

Die Lösung ist rel. einfach. Für ungerade Wurzel-Exponenten kann man die Wurzel aus dem Absolutwert der Basis ziehen und das Ergebnis dann mit dem Vorzeichenwert nochmal multiplizieren:

#include <math.h>

double x = -27.0;
double ax = fabs(x);
double y = pow(ax, (1.0 / 3.0)) * x / ax;

Das Problem besteht in der Umsetzung der pow-Funktion über Logarithmen:

pow(x,y) = (e^ln(x))^y = e^(y*ln(x))

d.h.

pow(-1,y) = e^(y*ln(-1)) --> der nat. Logarithmus akzeptiert keine neg. Argumente oder 0 --> pow domain error

Zeiger in Messages übergeben

Allgemein

Pointer in Messages weitergeben ist ganz einfach, wenn man folgendes beachtet:

SendMessage: Referenzierung von Objekten auf dem Stack, im statischen Speicher und auf dem Heap möglich
PostMessage: nur Referenzierung von Objekten im statischen Speicher und auf dem Heap möglich

Sender

// Handle auf eine bel. Form
HANDLE hForm = ...;

// einen Dateinamen zusammenbauen
char pcFileName[MAX_PATH];
strcpy(pcFileName, "c:\\test.txt");

// Nachricht WM_MAKESCREENSHOT absetzen und auf Ausführung warten
SendMessage(hMainForm, WM_MAKESCREENSHOT, 0, (LPARAM)&pcFileName);

Empfänger

void TfrmMain::MMakeScreenShot(TMessage M)
{
     TForm::Dispatch(&M);
     
     // Zeiger in LParam auslesen und in den gewünschten Typ wandeln
     char *pcFileName = (char*)M.LParam;
     AnsiString sFileName(pcFileName);

     MessageDlg(sFileName, mtInformation, TMsgDlgButtons() << mbOK, 0);
}

FileAttribute lesen und setzen

// faReadOnly  0x01  Schreibgeschützte Datei
// faHidden    0x02  Verborgene Datei
// faSysFile   0x04  Systemdatei
// faVolumeID  0x08  Laufwerks-ID
// faDirectory 0x010 Verzeichnis
// faArchive   0x020 Archivdatei
// faAnyFile   0x03F Beliebige Datei

// FileAttribute lesen
int iAttrs = FileGetAttr("test.txt");

if (iAttrs & faReadOnly)
{
    // wenn readonly gesetzt
}

// FileAttribute, z.B. 'readonly' und 'system', setzen
FileSetAttr("test.txt", faReadOnly | faSysFile);

Rootnodes in einem TreeView durchsuchen

// alle Rootnodes in einem TreeView durchsuchen
if (TreeView1->Items->Count -> 0)
{
   TTreeNode *tn = TreeView1->Items->Item[0];
   // solange eine gültige Rootnode gefunden wurde
   while (tn != NULL)
   {
       // hier irgendwas mit der Node 'tn' machen
       tn->Text = &quot;umbenannt&quot;;

       // nächste Rootnode suchen
       tn = tn->getNextSibling();
   }
}

TreeNodes anhand des Textes suchen

TTreeNode * __fastcall TForm1::GetNodeByText(AnsiString sSearchText)
{
      TTreeNode *pResultNode = NULL;
      TTreeNode *pNode = (TreeView1->Items->Count > 0) ? TreeView1->Items->Item[0] : NULL;

      // alle Nodes durchsuchen
      while (pNode != NULL)
      {
            if (UpperCase(pNode->Text) == UpperCase(sSearchText))
            {
                pResultNode = pNode;
                break;
            }

            // nächstes Item in der Liste TreeView1->Items heraussuchen
            pNode = pNode->GetNext();
      }

      return pResultNode;
}

In einem StringGrid auf Scrollereignis reagieren und anderes StringGrid per Code scollen

// Ereignis TopLeftChanged von StringGrid1 nutzen
void __fastcall TForm1::StringGrid1TopLeftChanged(TObject *Sender)
{
     // horizontale ScrollBarposition von StringGrid1 ermitteln
     int iScrollPosH = GetScrollPos(StringGrid1->Handle, SB_HORZ);
     // horizontale ScrollBarposition von StringGrid2 setzen
     SetScrollPos(StringGrid2->Handle, SB_HORZ, iScrollPosH, true);
     // Anzeige des Inhalts von StringGrid2 an die akt. ScrollBarposition anpassen
     StringGrid2->Perform(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, iScrollPosH), 0);

     // vertikale ScrollBarposition von StringGrid1 ermitteln
     int iScrollPosV = GetScrollPos(StringGrid1->Handle, SB_VERT);
     // vertikale ScrollBarposition von StringGrid2 setzen
     SetScrollPos(StringGrid2->Handle, SB_VERT, iScrollPosV, true);
     // Anzeige des Inhalts von StringGrid2 an die akt. ScrollBarposition anpassen
     StringGrid2->Perform(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, iScrollPosV), 0);
}

Formatierte, gedrehte, mehrzeilige Textausgabe

void __fastcall StringGridRotateTextOut(TStringGrid *Grid, int ARow, int ACol, TRect Rect, AnsiString sOutput, AnsiString sFontName, TColor clFontColor, int iFontSize, int iRotation)
{
     Grid->Canvas->Font->Name = sFontName;
     Grid->Canvas->Font->Size = iFontSize;
     Grid->Canvas->Font->Color = clFontColor;

     // neue Schrift für die gedrehte Ausgabe zusammenbauen
     TFont *tf = new TFont();
     TLogFont lf;
     tf->Assign(Grid->Canvas->Font);
     GetObject(tf->Handle, sizeof(lf), &lf);
     lf.lfEscapement = iRotation * 10;
     lf.lfOrientation = 0;
     HFONT hfont = CreateFontIndirect(&lf);
     tf->Handle = hfont;
     Grid->Canvas->Font->Assign(tf);
     delete tf;

     // Zelle malen
     Grid->Canvas->FillRect(Rect);
     // Text ausgeben
     // Beachten: abhängig vom Drehwinkel muss die X- und Y-Koordinate richtig gesetzt werden!
     // bei 90° z.B. Rect.Left + 2, Rect.Bottom - 2
     // DrawText bietet u.A. die Möglichkeit Text mehrzeilig (DT_WORDBREAK) auszugeben
     DrawText(Grid->Canvas->Handle, sOutput.c_str(), -1, &Rect, DT_VCENTER | DT_CENTER | DT_WORDBREAK);

     // Textausgabe könnte anstatt mit DrawText auch mit TextRect erfolgen,
     // es ist hierbei aber keine mehrzeilige Ausgabe möglich
     // Grid->Canvas->TextRect(Rect, Rect.Left + 2, Rect.Top + 2, sOutput);
}

void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
     TStringGrid *Grid = (TStringGrid *)Sender;
     Grid->Canvas->Font->Color = clBlack;

     if (ACol == 0)
     {
        // 1. Spalte "normal" fett und schwarz zeichnen
        Grid->Canvas->Font->Style = TFontStyles() << fsBold;
        Grid->Canvas->FillRect(Rect);
        DrawText(Grid->Canvas->Handle, Grid->Cells[ACol][ARow].c_str(), -1, &Rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
     }
     else
     {
         // alle anderen Spalten gedreht und verkleinert zeichnen
         Grid->Canvas->Font->Style = TFontStyles() >> fsBold;
         AnsiString sOutput = Grid->Cells[ACol][ARow] + "\n" + "noch eine Zeile :)";
         StringGridRotateTextOut(Grid, ARow, ACol, Rect, sOutput , "Arial", clBlack, 6, -10);
     }
}