14

Basically if one has a preloaded buffer for a null terminated string and the length to be referenced, and wants to pass a reference to it into a method that takes a std::string & but not copy the string or have it owned, is it possible to do so ?

This would only have a limited lifespan that is managed in such a way that it is only valid while the buffer is valid.

Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
peterk
  • 5,136
  • 6
  • 33
  • 47
  • 21
    Nope, no way. Your methods should've been taking `std::string_view`. – HolyBlackCat Feb 25 '22 at 07:14
  • 2
    What you are describing is a `string_view`. If you cannot change the member function you are calling (method is no C++ term) or their interfaces, you could theoretically create a class derived from `string` with your own implementation. However, the relevant functions in `basic_string` are not declared as `virtual` and cannot get new behaviour, when accessed as base class type. – Sebastian Feb 25 '22 at 07:42

2 Answers2

12

Is there a way make a std::string that references an externally provided buffer but not own it?

No.

You have these options:

  1. Use std::string as the "external" buffer in the first place.
  2. Copy the external buffer into the string.
  3. Don't use (reference to) std::string as the parameter.
    • std::string_view is a typically good choice. However, it's very easy to create non-null terminated string views, and your premise explicitly states null termination. If that's important, then you may need to avoid string view.
    • If string view isn't appropriate, then you can use const char* to point to the null terminated string.
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    Your first bullet under 3 is the wrong way round. The source is a null terminated string, and the destination wants something `std::string`-like, so it's fine. – Caleth Feb 25 '22 at 09:12
  • 3
    @Caleth It depends entirely on what the function does with it. `std::string` is null terminated too. – eerorika Feb 25 '22 at 09:14
  • Seems the answer is NO - that is you can not supply an external buffer to crate a std::string to pass into a method without copying the string contents. The reason I mentioned null termination is AFAIK the contract with std::string API is that it is null terminated. Also the reason is if one has an environment with another string type and wants to pass that into legacy methods that take a std::string without copying the string. – peterk Feb 26 '22 at 12:42
  • @peterk In the question of the OP all strings are null-terminated. Nevertheless they cannot be passed as std::string without copying. So this criterion is not a sufficient reason (it is only necessary). – Sebastian Feb 26 '22 at 12:51
2

Basically, the answer is no for the non owning string.

However, if the non owning criteria is not that important what you could do is to use your own allocator to reference a particular buffer.

What you also can do, is to use std::pmr::string which allows you to give a custom memory_resource.

The idea is as following :

#include <string>
#include <memory_resource>
#include <array>
#include <utility>
#include <iostream>

template<std::size_t size>
class StackBufferResource {
    public:
        auto as_resource() {return &m_resource;}
        auto address() {return m_buffer.data();}
    private:
        std::array<std::byte, size> m_buffer{};
        std::pmr::monotonic_buffer_resource m_resource{m_buffer.data(), size};
};

int main() {
    StackBufferResource<512> buffer;

    std::pmr::string myString("My name is Antoine and I am not sure for this answer", buffer.as_resource());
    std::cout << myString << "\n";
    std::cout << (const char*)buffer.address() << std::endl;
}

std::pmr::monotonic_buffer_resource is a memory_resource object which continually grows. It means that a deallocation is a kind of "no op".

What is nice with such a thing is that you can give the same thing to a std::pmr::vector.

However, you may pay the attention to the following points :

std::pmr::string use char. Since it is a trivial object, I think (I am not sure though) that it is safe to access the buffer memory after the string got destroyed. If it was a type not trivially destructible, I think it would be possible to see garbage values.

Antoine Morrier
  • 3,930
  • 16
  • 37
  • 1
    Yes probably the only way to have an allocator that will not delete the buffer and can assign it. I have done this with some other std::objects The the issue is to prevent a copy into the buffer and figure out a way to have it pre-loaded as the data already exists in it. A lot of legacy code for things take very large const std::string & as inputs and the copying becomes quite significant. – peterk Mar 01 '22 at 21:58
  • 1
    for sure you NEVER want to access freed memory - in the case I was wanting this it would be managed externally - the purpose is so one can pass large strings into library routines that do not own the string and will notify you when they no longer need it. const char *hugeText = getBufferFromSystem(locator); Like parseHugeText(maketmpString(hugeText)): systemRelease(hugeText); One could do this with an allocator that sets it to huge text and then does not free it. But only if the the std::string does not alter the contents of the buffer after it is "allocated". – peterk Mar 27 '22 at 18:00