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.

InformationsquelleAutor ZZYZX | 2013-01-21
Schreibe einen Kommentar