2009-10-19 18 views
6

Sırasıyla bir dizeyi ve uzunluğunu temsil eden char* ve sayısal değeri kabul eden düşük düzeyli bir API ile çalışıyorum. Kodum std::basic_string kullanıyor ve bu yöntemlere uygun çeviri ile çağrı yapıyor. Ne yazık ki, bu yöntemlerin birçoğu değişen boyuttaki dize uzunluklarını kabul eder (yani, max (unsigned char), max (short), vs.) ve dize örneklerimin belirtilen maksimum uzunluğu geçmediğinden emin olmak için yazı koduna takılıyorum Düşük seviyeli API ile.Bir uzunluk sınırlaması olan bir dizeyi uygulamak için bir tane std :: basic_string kaldırabilir mi?

Varsayılan olarak, std::basic_string Örneğin maksimum uzunluğu maksimum değeri ile bağlıdır size_t (ya max (unsigned int) ya da maksimum (__int64)). std::basic_string uygulamasının özelliklerini ve ayırma uygulamalarını manipüle etmenin bir yolu var, böylece kendi türümü size_t yerine kullanmak için belirtebilir miyim? Bunu yaparak, std::basic_string uygulamasında varolan sınır denetimlerini kullanmayı umuyorum, böylece çeviriyi gerçekleştirirken bunu yapmak zorunda kalmam.

Benim ilk soruşturma bu benim kendi dize sınıf yazmadan mümkün olmadığını göstermektedir, ama ben ebeveyn olarak

cevap

5

Özel bir ayırıcıyı, istediğiniz büyüklükte bir boyutu olan std::basic_string'a geçirebilirsiniz. Bu yeterli olmalı. Böyle Belki bir şey:

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

O zaman muhtemelen bunu yapabilirsiniz:

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

DÜZENLEME: Sadece beklendiği gibi bu çalıştığından emin olmak için bir test yaptık. Aşağıdaki kod bunu test eder.

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

son s += s bir std::bad_alloc istisna üst üzerine koydu ve neden olacaktır Yani, (benim sınırı 64k sadece kısa olduğundan). Ne yazık ki gcc'nin std::basic_string::max_size() uygulaması, kullandığınız ayırıcıya sonucunu dayandırmıyor, dolayısıyla daha fazla tahsis edebileceğini iddia ediyor. (Bunun bir hata olup olmadığından emin değilim ...).

Ancak bu, dizelerin boyutlarında basit bir şekilde zor sınırlar koymanıza kesinlikle izin verecektir. Maksimum boyutu bir şablon parametresi bile yapabilirsiniz, böylece yalnızca bir kez ayırıcı için kod yazmanız gerekir.

+1

Bir int şablon parametresi ekleyecektim ve max_size() return N; o zaman, typedef std :: basic_string , my_allocator > my256string; – jmucchiello

+0

'size_type' şablon argümanı olarak gösterilmesinin, kullanıcının hangi "size_type" öğesinin belirli bir string örneği için en uygun olduğunu seçmekte serbest olması yararlı olacaktır. Daha sonra kısmi şablon uzmanlığı, bu tür oyunların "basic_string" ile güzelleştirilmesinde yardımcı olacaktır. –

0

Eğer std sahip bir sınıf oluşturulamaz :) şey :: dize gözden kaçan ve umuyorum c_str() öğesini geçersiz kıl Veya kendi c_str16(), c_str32(), vb tanımlamak ve orada çeviri uygulamak?

+2

(sanal yöntemleri yoktur). Yani bu tavsiye edilmez. –

4

Onun çözümü hakkında Evan Teran'a katılıyorum. Bu artık sadece onun çözümün bir değişiklik geçerli:

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

Eğer myalloc ile hiç polimorfizm kullanmamalısınız unutmayın. Yani bu felaket:

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

ayrı bir türüdür sanki sadece onu kullanmak, aşağıdaki durumlarda güvenlidir:

standart kütüphanenin çoğu miras olması amaçlanmamıştır
myalloc<char, 1024> alloc; // max size == 1024 
+0

Evet, azami boyutu bir şablon parametresi yapmayı düşündüm. Ne yazık ki, çözümünüzün olduğu gibi çalışacağını düşünmüyorum. Çünkü (en az gcc's) string uygulaması, allocator'un maksimum büyüklüğünde maksimum boyutu temel almaz. Bu yüzden, 'max_size' bayttan daha fazlasını talep ettiğinde' (') atamasını yapmak zorunda kaldım. –

+0

@Evan Çözümün güzel, +1 btw'mi aldın. Açıkladığınız gibi 'ayırma' için daha fazla kod ekleyeceğim :) – AraK

İlgili konular