可以利用 std::basic_string 来实现具有长度限制的字符串吗?

发布于 2024-08-08 11:07:17 字数 548 浏览 3 评论 0原文

我正在使用一个低级 API,它接受 char* 和数值来分别表示字符串及其长度。我的代码使用 std::basic_string 并通过适当的转换调用这些方法。不幸的是,其中许多方法接受不同大小的字符串长度(即 max(unsigned char)、max(short) 等...),而我一直在写代码以确保我的字符串实例不超过低级 API 规定的最大长度。

默认情况下,std::basic_string 实例的最大长度受 size_t 的最大值(max(unsigned int) 或最大(__int64))。有没有办法操纵 std::basic_string 实现的特征和分配器实现,以便我可以指定自己的类型来代替 size_t ?通过这样做,我希望利用 std::basic_string 实现中的任何现有边界检查,这样我在执行翻译时就不必这样做。

我的初步调查表明,如果不编写自己的字符串类,这是不可能的,但我希望我忽略了一些东西:)

I'm working with a low-level API that accepts a char* and numeric value to represent a string and its length, respectively. My code uses std::basic_string and calls into these methods with the appropriate translation. Unfortunately, many of these methods accept string lengths of varying size (i.e. max(unsigned char), max(short), etc...) and I'm stuck writing code to make sure that my string instances do not exceed the maximum length prescribed by the low-level API.

By default, the maximum length of an std::basic_string instance is bound by the maximum value of size_t (either max(unsigned int) or max(__int64)). Is there a way to manipulate the traits and allocator implementations of a std::basic_string implementation so that I may specify my own type to use in place of size_t? By doing so, I am hoping to leverage any existing bounds checks within the std::basic_string implementation so I don't have to do so when performing the translation.

My initial investigation suggests that this is not possible without writing my own string class, but I'm hoping that I overlooked something :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

歌枕肩 2024-08-15 11:07:17

您可以将自定义分配器传递给 std::basic_string ,它的最大大小为您想要的任何值。这应该足够了。也许是这样的:

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

那么你可以这样做:

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

编辑:我刚刚做了一个测试,以确保它按预期工作。下面的代码对其进行测试。

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

最后一个 s += s 会将其放在顶部并导致 std::bad_alloc 异常(因为我的限制仅低于 64k)。不幸的是,gcc 的 std::basic_string::max_size() 实现并不将其结果基于您使用的分配器,因此它仍然声称能够分配更多。 (我不确定这是否是一个错误......)。

但这肯定会让您以简单的方式对字符串的大小施加硬性限制。您甚至可以将最大大小设置为模板参数,这样您只需为分配器编写一次代码。

you can pass a custom allocator to std::basic_string which has a max size of whatever you want. This should be sufficient. Perhaps something like this:

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

Then you can probably do this:

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

EDIT: I've just done a test to make sure this works as expected. The following code tests it.

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

That last s += s will put it over the top and cause a std::bad_alloc exception, (since my limit is just short of 64k). Unfortunately gcc's std::basic_string::max_size() implementation does not base its result on the allocator you use, so it will still claim to be able to allocate more. (I'm not sure if this is a bug or not...).

But this will definitely allow you impose hard limits on the sizes of strings in a simple way. You could even make the max size a template parameter so you only have to write the code for the allocator once.

很酷不放纵 2024-08-15 11:07:17

我同意 Evan Teran 的解决方案。这只是他的解决方案的修改:

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

请注意,您根本不应该在 myalloc 中使用多态性。所以这是灾难性的:

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

你只需使用它就好像它是一个单独的类型一样,在以下情况下它是安全的:

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

I agree with Evan Teran about his solution. This is just a modification of his solution no more:

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

Be aware you should not use polymorphism at all with myalloc. So this is disastrous:

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

You just use it as if it is a separate type, it is safe in following case:

myalloc<char, 1024> alloc; // max size == 1024
﹎☆浅夏丿初晴 2024-08-15 11:07:17

你不能创建一个以 std::string 作为父类并覆盖 c_str() 的类吗?
或者定义您自己的 c_str16()、c_str32() 等并在那里实现翻译?

Can't you create a class with std::string as parent and override c_str()?
Or define your own c_str16(), c_str32(), etc and implement translation there?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文