Grundsätzlich: Strings als Übergebeparameter
-
Roger Wilco schrieb:
Würdet Ihr im Falle der Windows-Programmierung, wo man viel mit LPCSTR/LPCWSTR als Parametertyp zu tun hat und intern mit std::wstring arbeitet, für eigene Schnittstellen die C-String-Zeiger (LPCWSTR) oder C++-String-Referenzen (const std::wstring&)benutzen?
Wie schon oben gesagt würde ich grundsätzlich plattformunabhängige Schnittstellen bereitstellen. Die einzige Ausnahme ist, wenn du die Schnittstellen der WinAPI übergeben musst, damit diese die Funktionen aufrufen kann. Da die WinAPI aber ein C-API ist wirst du dafür sowieso nicht die Klassenmethoden direkt benutzen können.
Der Vorteil bei plattformunabhängigen Schnittstellen ist folgender: Auch wenn du im Moment die Implementierungen deiner Klassen stark windows-spezifisch machst, kannst du später trotzdem z.B. eine Linuxspezifische Implementierung dazu schreiben, die du dann mit linken kannst, ohne die Schnittstelle selber ändern zu müssen.
-
Danke pumuckl, Recht hast Du.
Ich denke auch std::strings als const. reference zu benutzen ist auch einfach fehlersicherer. Die LPCWSTR-Zeiger können ja sonst wo hinzeigen (auch NULL sein) und man kann sich nicht sicher sein, dass die Strings auch initialisiert und nullterminiert sind.
-
Roger Wilco schrieb:
Ich denke auch std::strings als const. reference zu benutzen ist auch einfach fehlersicherer. Die LPCWSTR-Zeiger können ja sonst wo hinzeigen (auch NULL sein) und man kann sich nicht sicher sein, dass die Strings auch initialisiert und nullterminiert sind.
Wenn du aber einen
wchar_t*übergibst, wird der in einenstd::wstringkonvertiert - egal, ob er auf einen gültigen Speicherbereich, auf Null oder in ein schwarzes Loch zeigt.std::wstringwird dann die entsprechenden Probleme haben...Wenn es also möglich ist, Nullzeiger zu übergeben, brauchst du doch eine Funktion, die einen
wchar_t*nimmt und diesen prüft. Gegen wilde Zeiger kannst du nicht viel machen, da musst du halt schauen, dass diese nicht vorkommen, was bei guter Programmierung eigentlich im Bereich des Möglichen liegt.
-
Nexus schrieb:
Wenn es also möglich ist, Nullzeiger zu übergeben, brauchst du doch eine Funktion, die einen
wchar_t*nimmt und diesen prüft. Gegen wilde Zeiger kannst du nicht viel machen, da musst du halt schauen, dass diese nicht vorkommen, was bei guter Programmierung eigentlich im Bereich des Möglichen liegt.Das ist eine Frage des Schnittstellendesigns. Tendenziell würde ich die Schnittstelle eher schmal halten, sprich nur std::wstring. Es ist dann Sache des Aufrufers bei eventuell auftretenden wchar_t-Pointern die nötigen Überprüfungen vorzunehmen. Idealerweise sollte eine Schnittstelle nur die nötigen Funktionen bereitstellen und nicht alle möglicherweise irgendwann mal auftretenden Fälle abdecken. In der Praxis macht man natürlich Kompromisse - wenn ein Fall häufig auftaucht, der von der minimalen Schnittstelle nicht abgedeckt wird, dann erweitert man die Schnittstelle unter Umständen entsprechend. Im vorliegenden Fall würde ich aber eher drauf verzichten und lediglich in die Doku der Schnittstelle schreiben, dass die wstrings gewissen Einschränkungen unterliegen (z.B. keine wchar_t-Nullpointer).
-
Kommt ja auch eher darauf an, was man entwickelt. Wenn ich eine Library (besonders für andere Projekte) entwickle, wird meine Schnittstelle Fälle voraussehen müssen. Damit auch mehr Projekte damit etwas anfangen können.
Wenn es eher um Schnittstellen für geschlossene Systeme geht (z.B. innerhalb einer Anwendung), dann mache ich nur die nötigsten Schnittstellen. Weil wenn ich was brauche, kann ich die ruck zuck selber hinzufügen.
-
@pumuckl: Stimmt...

@all: Und welche Variante bevorzugt ihr für Strings als Rückgabewert?
std::wstring GetFilename(); std::wstring configFile = GetFilename();bool GetFilename(std::wstring& filename); std::wstring configFile; if (!GetFilename(configFile)){ ... }
-
variante 1.
welchen sinn sollte variante 2 haben?
-
Roger Wilco schrieb:
@all: Und welche Variante bevorzugt ihr für Strings als Rückgabewert?
Kommt drauf an, das ist eine Frage der Fehlerbehandlung. Wenn es "normal" ist, dass kein Filename zurückgegeben werden kann, ist die zweite Version durchaus legitim (oder die erste mit leerem String, um anzuzeigen dass da nix kommt), andernfalls wenn eigentlich immer ein Filename zurückgegeben werden kann, die erste Version und im Notfall eine Exception werfen.
-
Shade Of Mine schrieb:
welchen sinn sollte variante 2 haben?
Ich dachte sie wäre (minimal) schneller, da keine Kopie gemacht wird. (Zusätzlich hat man gleich eine kompakte SUCCESS/FAILURE-Behandlungsmöglichkeit.)
-
Roger Wilco schrieb:
Ich dachte sie wäre (minimal) schneller, da keine Kopie gemacht wird. (Zusätzlich hat man gleich eine kompakte SUCCESS/FAILURE-Behandlungsmöglichkeit.)
Unter Laborbedienungen wäre die Variante mit direktem Return vermutlich schneller. In der freien Wildbahn hängt es von zuvielen Faktoren ab...
SUCCESS/FAILURE wird mittels exceptions gelöst. Return Werte sind sowas von letztem jahrhundert...

-
Ich mache immer die erste Variante. In vielen Fällen optimiert der Compiler das Kopieren nämlich weg! Gibts glaube ich sogar einen Begriff dafür, wenn ein Rückgabewert wegoptimiert werden kann.
Bei sowas sollte man nicht versuchen schlauer zu sein als Compiler, wenn es auf Codeverständnis geht.
Weiterhin würde ich bei einem unerwarteten Fehler eine Exception werfen.
-
Vielen Dank soweit für Eure Meinungen und Ratschläge!
Zum Thema Exceptions: Davor habe ich mich bisher immer gedrückt.
Das Prinzip ist mir klar, aber wie werden die in einem Projekt sinnvoll (einheitlich, plausibel etc.) eingesetzt. Mir fehlen da einfach Ansätze für ein Prinzip, eine einheitliches und sinnvolles Verfahren.
Das fängt ja an beim Design von Exceptions, deren Klassifizierung etc...
Daher noch die alte Returnwert-Prüfung zur Fehlerbehandlung.
Wenn es da hilfreiche Links oder Literatur gibt, wäre ich sehr dankbar.
-
SUCCESS/FAILURE wird mittels exceptions gelöst. Return Werte sind sowas von letztem jahrhundert...
Hmm, Exceptions sind fuer Ausnahmen da. Bei z.B. falschen Nutzereingaben fuer Dateien sollte keine Exception geworfen werden. Auch sollte bei Ressourcemanagement auf Exceptions verzichtet werden. Java/C# gehen da andere Wege als C++. Returnwerte sind also sowas von nicht letztes Jahrhundert.
-
Roger Wilco schrieb:
Zum Thema Exceptions: Davor habe ich mich bisher immer gedrückt.
...
Das fängt ja an beim Design von Exceptions, deren Klassifizierung etc...
Daher noch die alte Returnwert-Prüfung zur Fehlerbehandlung.
Naja, Returnwerte musst du auch Klassifizieren (Ohne zu wissen wofür ein Rückgabewert steht, kann man diesen auch nicht verwerten), und vor allem lassen sich Rückgabewerte sehr einfach "vergessen".
cu André
-
knivil schrieb:
Hmm, Exceptions sind fuer Ausnahmen da. Bei z.B. falschen Nutzereingaben fuer Dateien sollte keine Exception geworfen werden. Auch sollte bei Ressourcemanagement auf Exceptions verzichtet werden. Java/C# gehen da andere Wege als C++. Returnwerte sind also sowas von nicht letztes Jahrhundert.
Ich sagte im Falle von Failure/Success verwendet man exception. wenn nix fehlschlägt, fliegt auch keine exception...
und gerade bei resourcen management verwendet man exceptions - stichwort: RAII/RRID
@Roger Wilco:
siehe http://magazin.c-plusplus.net/artikel/Modernes Exception-Handling Teil 1 - Die Grundlagen
-
knivil schrieb:
Hmm, Exceptions sind fuer Ausnahmen da. Bei z.B. falschen Nutzereingaben fuer Dateien sollte keine Exception geworfen werden.
richtig.
knivil schrieb:
Returnwerte sind also sowas von nicht letztes Jahrhundert.
falsch.
denn GetFilename sollte nicht aufgerufen werden, wenns nur daran lag, daß der benutzer dumme sachen eingegeben hat. da wäre eine exception schlecht. und der returnwert unnötig. assert wäre gut.
aber GetFilename könnte auch fehlschlagen wegen eines lesefehlers auf der platte. sowas unerwartetes fordert ne exception. willst du alle unerwarteten sachen mit fehlerrückgaben und if hochreichen, bläht sich dein code aufs doppelte auf und wird auch nicht mehr fein lesbar sein. geschweige denn wartbar.
-
knivil schrieb:
Auch sollte bei Ressourcemanagement auf Exceptions verzichtet werden.
Weswegen? RAII arbeitet ja gerade auf Basis von Scopes und Exceptions (Bzw. erhält man dank RAII meist Exceptionsicherheit).
knivil schrieb:
SUCCESS/FAILURE wird mittels exceptions gelöst. Return Werte sind sowas von letztem jahrhundert...
Hmm, Exceptions sind fuer Ausnahmen da. Bei z.B. falschen Nutzereingaben fuer Dateien sollte keine Exception geworfen werden.
Kommt darauf an. Wenn man die fehlerhaften Nutzereingaben direkt an Ort und Stelle wieder beheben kann (z.B. um nochmalige Eingabe bitten) sind Exceptions fehl am Platz. Wenn aber die Fehlangaben an dieser Stelle nicht korrigiert werden können kann eine Exception durchaus Sinn machen.
Grob gesagt würde ich Exceptions im Fehlerfall, Rückgabewerte im Statusfall anwenden (Und bei Gettern beispielsweise ist für mich der Rückgabewert immer das, was ich von der Getter haben will. Wenn ich keine Daten erhalten kann, wiederspricht dies einer Funktion einer Getter-Methode und ich sehe dies als Fehler - als Exception - an).
cu André
-
pumuckl schrieb:
Das ist eine Frage des Schnittstellendesigns. Tendenziell würde ich die Schnittstelle eher schmal halten, sprich nur std::wstring. Es ist dann Sache des Aufrufers bei eventuell auftretenden wchar_t-Pointern die nötigen Überprüfungen vorzunehmen.
Um das genau zu sagen, müsste man die Aufgaben der Klasse kennen. Wenn innerhalb ein
wchar_t*verwendet wird und dessen Wert optional ist (also entweder ein gültiger WString oder Null), würde ich an der Schnittstelle auch eine Funktion mitwchar_t*-Parameter hinzufügen. Weil eben auch Null möglich ist.Zum Thema Exceptions ist es eigentlich ähnlich; wenn man erwartet, dass eine Funktion einen String zurückliefert, dann sollte im Fehlerfall eine Exception geworfen werden. Wenn es aber darum geht, irgendeinen Status abzufragen, der entweder nicht verwendet wird (Null) oder einen WString repräsentiert (gültiger
wchar_t*), ist auch ein Rückgabewert angebracht.
-
Ich kann wunderbar RAI machen ohne auch nur an Exceptions zu denken. RAI hat in erster Linie nichts mit Exceptions zu tun.
-
knivil schrieb:
Ich kann wunderbar RAI machen ohne auch nur an Exceptions zu denken. RAI hat in erster Linie nichts mit Exceptions zu tun.
RAII mit doppel I!
Natuerlich geht es auch ohne Exception, aber dann hat man wieder ein paar der Probleme die man ohne RAII auch hat. Und das will man ja eigentlich nicht

Lies dir mal meinen Artikel ueber Exceptions durch.
Am Ende gehe ich auch kurz auf alternativen ein - aber Exceptions und RAII spielen halt wundertoll zusammen - so dass man eigentlich fahrlaessig handelt es nicht auszunutzen...