C++: die Übergabe von Klassen an vararg-Funktion
Ich versuche, mich eine Klasse, verhält sich wie die MS CString (das heißt, Sie übergeben es an printf und es verhält sich genau wie ein Zeiger auf C-string, ohne zusätzliche hässliche schwarze Magie wie ".c_str()").
Dies ist die erste Implementierung dieser Klasse, die einfach nur funktioniert und nicht noch etwas nützliches:
#include <cstdlib>
#include <cstring>
class CString
{
protected:
struct CStringInfo
{
size_t Length;
size_t MaxLength;
};
public:
CString()
{
Buffer = NULL;
Assign(NULL);
}
CString(const char* chv)
{
Buffer = NULL;
Assign(chv, 0);
}
~CString()
{
if(Buffer) delete[] Buffer;
Buffer = NULL;
}
size_t GetLength()
{
if(!Buffer) Alloc(1);
return GetInfo()->Length;
}
size_t Resize(size_t size)
{
Alloc(size + 1); //+ 0x00
Buffer[size] = 0;
return size;
}
bool Assign(const char* value, size_t size = 0)
{
size_t strl = ((size) ? size : strlen(value));
if(!value || !(strl = strlen(value)))
{
if(!Buffer) Alloc(1);
return false;
}
Alloc(strl + 1);
memcpy(Buffer, value, strl);
Buffer[strl] = 0;
return true;
}
CString& operator = (const char* what)
{
Assign(what);
return (*this);
}
CString& operator = (CString& string)
{
Assign(string.Buffer);
return (*this);
}
operator const char* ()
{
return Buffer;
}
protected:
char* Buffer;
void Alloc(size_t size)
{
if(!size) size = 1;
char* nb = new char[size + sizeof(CStringInfo)];
char* nbb = nb + sizeof(CStringInfo);
size_t cl = size - 1;
if(Buffer)
{
if(cl > GetInfo()->Length) cl = GetInfo()->Length;
if(cl) memcpy(nbb, Buffer, cl - 1);
nbb[cl] = 0;
*(CStringInfo*)(nb) = *(CStringInfo*)(Buffer);
delete[] (Buffer - sizeof(CStringInfo));
}
Buffer = nb;
GetInfo()->MaxLength = size;
GetInfo()->Length = cl;
}
void Free()
{
if(Buffer)
{
delete[] (Buffer - sizeof(CStringInfo));
}
}
CStringInfo* GetInfo()
{
return (CStringInfo*)(this->Buffer - sizeof(CStringInfo));
}
};
Und code, den ich testen Sie es auf:
#include <cstdio>
#include "CString.hpp"
CString global_str = "global string!";
int main(int argc, char* argv[])
{
CString str = "string";
printf("Test: %s, %s\n", str, global_str);
return 0;
}
Wenn ich nicht ein Destruktor in der Klasse, dann kann ich es weiterleiten zu printf und es wird funktionieren, wie es sollte (als C-string). Aber wenn ich hinzufügen Destruktor, den GCC erzeugt folgenden Fehler:
error: cannot pass objects of non-trivially-copyable type 'class CString' through '...'
Werden, und zusätzlich zu, dass vor GCC Versionen geben wird, eine Verwarnung + ud2 opcode.
Also... Frage: kann ich eigentlich die folgenden Bauarbeiten in den GCC oder gibt es eine Möglichkeit, die möglicherweise nicht mit C varargs, um etwas identisch, um die oben genannten code?
- Das erste, was ich ändern würde ist die
const char* operator
ständige:const char* operator() const
-- Dies könnte sogar vollständig das problem lösen, aber ich bin mir nicht sicher. Bitte versuchen Sie es. Hinzufügen eines Destruktor macht die Klasse nicht-POD (wenn es ohne ein), bedeutet dies, es ist nicht-trivial-kopierbar. - Gut, habe es gerade getestet, dies nicht helfen. Es scheint unmöglich zu sein... (Aber die const-ness ist noch immer gut hier!)
- Ich weiß nicht, warum würden Sie wollen, dieses Verhalten. Implizite Umwandlung von nicht-trivialen Arten auf variable argument-Listen ist genau die Art von schlechten Angewohnheit, dass C++ wurde entworfen, um zu stomp out.
- Betrachten Sie folgende situation:
CString str1; CString str2 = "text"; str1.Format("%s", str2.GetBuffer());
Eine Klasse, die Formate selbst in sich selbst mit einem Drittanbieter-Konvertierung. Wie schön. Was ich nicht verstehe ist, warum nicht fügen Sie etwas wie-operator... (), die steuert, welche tatsächlich verwendet wird, wenn Sie übergeben eine Klasse über vararg. - Ich bin mir ziemlich sicher, dass MS CString verwendet einige clevere interne Trickserei zu erkennen und verarbeiten von Instanzen von CObject, dass es kann auch an Stelle von %s an. Es gibt keine Weise, die Sie simulieren können, ohne Ihre eigenen Rollen
printf
. - Ja, aber auch wenn ich Schreibe, meine eigene printf (TBH, ich werde nicht, es zu tun), ich muss mich übergeben das Objekt an eine Funktion mit ellipsis-operator nicht erlaubt ist.
- Äh, ja, guter Punkt. Müssen Rollen Sie Ihre eigenen printf-und complier, dann, glaube ich. (das ist, was Microsoft getan hat, im wesentlichen.. heh).
- Was @paddy sagte. Bitte tun Sie das nicht.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ausgelöst werden kann, die conversion-operator mit einem cast:
Aber ich weiß nicht wenn Sie in irgendwelche Probleme mit diesem, die Vermeidung von varargs in C++ - code wäre wahrscheinlich die beste.
Wie über die Verwendung der typsichere printf statt (Credit: Wikipedia):
Ich glaube nicht, dass libstdc++ unterstützt
std::runtime_error
undstd::logic_error
obwohl.Du ziemlich viel brauchen, um auf eine member-Funktion, die entweder direkt (
foo.c_str()
) oder über so etwas wie einen cast ((char *)foo
).Ansonsten kommt es auf den compiler an. In C++03, ist das Verhalten undefiniert (§5.2.2/7):
...aber in (C++11, §5.2.2/7), es ist bedingt unterstützt:
"bedingt unterstützt, die mit der Implementierung definierte Semantik" verlässt eine öffnung für eine Durchführung zu unterstützen, es mit der richtigen Dokumentation, aber es ist immer noch so nah zu undefinierten Verhalten, wie Sie bekommen können.
Wenn ich im Begriff waren, dies zu tun, ich denke, ich würde einzurichten irgendeine Art von Vermittler mit ein variadic template. Mit diesem, Sie würden die Versorgung einer überlastung, die (zum Beispiel) automatisch übergeben
foo.c_str()
zuprintf
wenn Sie übergeben ein argument vom Typstd::string
. Es ist (wahrscheinlich) mehr code, aber zumindest ist es Ihnen tatsächlich arbeiten. Ich persönlich möchte vermeiden, die ganze Sache als einfach nur mehr ärger als es Wert ist wenn.was sind Sie versuchen zu lösen, diese schriftlich (hässlich, um ehrlich zu sein) der string-Klasse??
warum nicht verwenden Sie etwas anderes? (wie
std::string
) - bitte denken Sie zweimal, bevor Sie zu schreiben beginnen Ihre eigenen super-optimierte Saiten...zu Ihrer Frage: Sie sind wirklich Glück mit deinem Beispiel-code! haben Sie eine Ahnung, wie das ellipse-Werke (Maschinen-code) in C und warum es darf nicht passieren, nicht-triviale Arten über es?
In Kürze:
printf()
sieht einfach in ein format-string, und wenn er sieht '%s' in es es wird davon ausgegangen, dass das nächste argument ist einchar*
thats all! Also wenn du an etwas anderem (wiechar
,short
usw.) statt-es wird UB! (und im Falle von verschiedenensizeof()
als erwartet, es besteht eine hohe Wahrscheinlichkeit, dass Sie mit einem "segmentation fault" bald... Es ist der Grund, warum Ellipsen ist eine schlechte Praxis in C++! Sie sind komplett Typ-unsicher!Wenn Sie mit C++, weiß nur nicht C-API! Es gibt genügend C++ - libs entwickelt, die für die Formatierung der Ausgabe (wie boost::format) und Sie sind Typ-sicher! C++11 öffnen Sie die Tür für printf-like-Funktion, aber mit Typ-sicher-Garantie! gerade gelesen, dass eine "klassische" Beispiel über variadic templates... und nur nach dem Lesen, versuchen zu implementieren Ihre eigenen Saiten 🙂
Ihnen nicht passieren können Objekte per varargs, nur Zeiger auf Objekte. Jedoch, Sie können eine (Variable) template-basierte Implementierung von
printf
wie die von C++ - Format:Diese wird print "Test: string, global string!" zur Verfügung gestellt, die
CString
richtig umgesetzt wird.Im Gegensatz zu Jesse Gute Umsetzung dieses unterstützt die standard
printf ()
- Formatbezeichner.Haftungsausschluss: ich bin der Autor dieser Bibliothek