Kann man Hebelwirkung std :: basic_string eine Zeichenfolge zu implementieren eine Längenbegrenzung zu haben?

StackOverflow https://stackoverflow.com/questions/1591591

Frage

Ich arbeite mit einem Low-Level-API, die ein char* und numerischer Wert akzeptiert eine Zeichenfolge und seine Länge zu repräsentieren. Mein Code verwendet std::basic_string und Anrufe in diese Methoden mit der entsprechenden Übersetzung. Leider nehmen viele dieser Methoden Stringlängen unterschiedlicher Größe (dh max (unsigned char), max (short), etc ...) und ich bin Code stecken schriftlich um sicherzustellen, dass meine String-Instanzen nicht überschreiten die maximale Länge vorgeschrieben durch den Low-Level-API.

Standardmäßig ist die maximale Länge einer std::basic_string Instanz wird durch den Maximalwert von size_t (entweder max (unsigned int) oder max (__int64)) gebunden. Gibt es eine Möglichkeit, die Eigenschaften und Allocator Implementierungen einer std::basic_string Implementierung so zu manipulieren, dass ich meine eigene Art zu verwenden anstelle von size_t angeben kann? Auf diese Weise hoffe ich alle vorhandenen Grenzen Kontrollen innerhalb der std::basic_string Implementierung zu nutzen, also muss ich dies nicht tun, wenn die Übersetzung durchführen.

Meine erste Untersuchung legt nahe, dass dies nicht möglich ist, ohne meine eigene String-Klasse zu schreiben, aber ich hoffe, dass ich etwas übersehen:)

War es hilfreich?

Lösung

können Sie eine benutzerdefinierte allocator zu std::basic_string passieren, die eine maximale Größe hat, was Sie wollen. Dies sollte ausreichend sein. Vielleicht so etwas wie folgt aus:

template <class T>
class my_allocator {
public:
    typedef T              value_type;

    typedef std::size_t    size_type;
    typedef std::ptrdiff_t difference_type;
    typedef T*             pointer;
    typedef const T*       const_pointer;
    typedef T&             reference;
    typedef const T&       const_reference;

    pointer address(reference r) const             { return &r; }
    const_pointer address(const_reference r) const { return &r; }

    my_allocator() throw() {}

    template <class U>
    my_allocator(const my_allocator<U>&) throw() {}

    ~my_allocator() throw() {}

    pointer allocate(size_type n, void * = 0) {
        // fail if we try to allocate too much
        if((n * sizeof(T))> max_size()) { throw std::bad_alloc(); }
        return static_cast<T *>(::operator new(n * sizeof(T)));
    }

    void deallocate(pointer p, size_type) {
        return ::operator delete(p);
    }

    void construct(pointer p, const T& val) { new(p) T(val); }
    void destroy(pointer p)                 { p->~T(); }

    // max out at about 64k
    size_type max_size() const throw() { return 0xffff; }

    template <class U>
    struct rebind { typedef my_allocator<U> other; };

    template <class U>
    my_allocator& operator=(const my_allocator<U> &rhs) {
        (void)rhs;
        return *this;
    }
};

Dann können Sie wahrscheinlich tun:

typedef std::basic_string<char, std::char_traits<char>, my_allocator<char> > limited_string;

EDIT: Ich habe gerade einen Test getan, um diese Arbeiten, um sicherzustellen, wie erwartet. Der folgende Code testet es.

int main() {
    limited_string s;
    s = "AAAA";
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s; // 512 chars...
    s += s;
    s += s;
    s += s;
    s += s;
    s += s;
    s += s; // 32768 chars...
    s += s; // this will throw std::bad_alloc

    std::cout << s.max_size() << std::endl;
    std::cout << s.size() << std::endl;
}

Das letzte s += s es über die Spitze gesetzt wird und eine std::bad_alloc Ausnahme verursachen, (da mein Limit knapp 64k ist). Leider std::basic_string::max_size() Implementierung des gcc nicht das Ergebnis auf der Basis allocator Sie verwenden, so wird es noch mehr zuordnen zu können, verlangen. (Ich bin nicht sicher, ob dies ein Fehler ist oder nicht ...).

Das wird aber auf jeden Fall können Sie harte Grenzen für die Größen von Strings auf einfache Art und Weise verhängen. Man könnte sogar die maximale Größe einen Template-Parameter, so dass Sie nur einmal den Code für das Allocator schreiben.

Andere Tipps

Ich stimme mit Evan Teran über seine Lösung. Dies ist nur eine Modifikation seiner Lösung nicht mehr:

template <typename Type, typename std::allocator<Type>::size_type maxSize>
struct myalloc : std::allocator<Type>
{
    // hide std::allocator[ max_size() & allocate(...) ]

    std::allocator<Type>::size_type max_size() const throw()
    {
        return maxSize;
    }
    std::allocator<Type>::pointer allocate
        (std::allocator<Type>::size_type n, void * = 0)
    {
        // fail if we try to allocate too much
        if((n * sizeof(Type))> max_size()) { throw std::bad_alloc(); }
        return static_cast<Type *>(::operator new(n * sizeof(Type)));
    }
};

Beachten Sie nicht Polymorphismus überhaupt mit myalloc verwenden sollten. Also das ist katastrophal:

// std::allocator doesn't have a virtual destructor
std::allocator<char>* alloc = new myalloc<char>;

Sie es einfach verwenden, als ob es eine separate Art ist, ist es sicher in folgendem Fall:

myalloc<char, 1024> alloc; // max size == 1024

Können Sie nicht eine Klasse mit std :: string erstellen als Eltern und Überschreibung c_str ()? Oder definieren Sie Ihre eigene c_str16 (), c_str32 (), etc. und implementieren Übersetzung da?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top