[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);
}

Error: The Side-by-Side configuration information for … (Errorcode: 14001)

Nach der Weitergabe und Start von Anwendungen und DLLs, die in Visual Studio erstellt wurden, kommt es häufig zu folgender Fehlermeldung:

Error: The Side-by-Side configuration information for „yourdll.dll“ contains errors. Diese Anwendung konnte nicht gestartet werden, weil die Anwenungskonfiguration nicht korrekt ist. Zur Problembehebung sollten Sie die Anwendung neu installieren (14001).

Diese Meldung kann man ausschalten, wenn man in Visual Studio statisch gegen die C-Runtime (CRT) linkt: Link
Kurz zusammengefasst muss man folgende Einstellungen vornehmen:

  • Projekt->Eigenschaften->C/C++->Codeerzeugung->Laufzeitbibliothek->Multi-threaded debug (/MTd) (Debug) und Multi-threaded (/MT) (Release)

Weiterführend zum Errorcode 14001 in Bezug auf die Abhängigkeit von dwmapi.dll: Link