Can a char* be moved into an std::string?
Asked Answered
V

2

6

Say I have something like this

extern "C" void make_foo (char** tgt) {
  *tgt = (char*) malloc(4*sizeof(char));
  strncpy(*tgt, "foo", 4);
}

int main() {
  char* foo;
  make_foo(&foo);
  std::string foos{{foo}};
  free(foo);
  ...
  return 0;
}

Now, I would like to avoid using and then deleting the foo buffer. I.e., I'd like to change the initialisation of foos to something like

  std::string foos{{std::move(foo)}};

and use no explicit free.

Turns out this actually compiles and seems to work, but I have a rather suspicious feel about it: does it actually move the C-defined string and properly free the storage? Or does it just ignore the std::move and leak the storage once the foo pointer goes out of scope?

It's not that I worry too much about the extra copy, but I do wonder if it's possible to write this in modern move-semantics style.

Vacuva answered 3/6, 2018 at 12:23 Comment(9)
It "ignores" the std::move (it moves the const char *, not the actual data) and leak. You cannot use a predefined buffer with std::stringRetroversion
I think first level of incompatibility is malloc/free in C++Larousse
It's impossible to do what you want (in standard C++).Bechtold
@JacekCz what do you mean – I should be using #include <cstdlib> and then std::free? I certainly can't do anything about the malloc in the C code, and why would this be a problem anyway?Vacuva
@Vacuva They probably meant that even if std::string did take ownership it would call the destroy for the default allocator which calls delete and not free. Mixing delete with malloc is a no-no.Miraculous
if you are able, consider std::string_view (c++17)Jaleesa
Just for the record, std::move doesn't actually move anything, it just performs a cast.Dirigible
This doesn't address the question, but using strncpy here is belt-and-suspenders programming, with broken suspenders. The code allocates an array of 4 char, and copies 4 characters into it. Fine, no problem. But if it was modified to copy 5 characters instead, strncpy would happily stop copying after the first 4 characters then return, producing an unterminated char array. That will almost certainly result in a memory access out of bounds later in the program. strncpy is not a "safe" replacement for strcpy for any reasonable definition of "safe".Clayborne
@PeteBecker comment appreciated, I'm rubbish at C programming. (In my actual use case, the string doesn't come from C but from Haskell, through an automatically-generated C interface, so I don't need to worry about that kind of pitfall.)Vacuva
R
4

tl;dr: Not really.

Pointers don't have any special move semantics. x = std::move(my_char_ptr) is the same as x = my_char_ptr. They are not similar in that regard to, say, std::vector's, in which moving takes away the allocated space.

However, in your case, if you want to keep existing heap buffers and treat them as strings - it can't be using std::string's, as they can't be constructed as a wrapper of an existing buffer (and there's small-string optimization etc.). Instead, consider either implementing a custom container, e.g. with some string data buffer (std::vector<char>) and an std::vector<std::string_view>, whose elements point into that buffer.

Remuneration answered 3/6, 2018 at 12:36 Comment(1)
@SombreroChicken: ?Remuneration
M
6

std::string constructor #5:

Constructs the string with the contents initialized with a copy of the null-terminated character string pointed to by s. The length of the string is determined by the first null character. The behavior is undefined if s does not point at an array of at least Traits::length(s)+1 elements of CharT, including the case when s is a null pointer.

Your C-string is copied (the std::move doesn't matter here) and thus it is up to you to call free on foo.

A std::string will never take ownership.

Miraculous answered 3/6, 2018 at 12:28 Comment(1)
More fundamental: resolving ownership of char * argument is impossible. Maybe this is nth character of "abcde"+2, maybe on the stack, maybe allocated. Languages hahivng concept of "string owneship" are totally diferent. Opposite illustration from C++ world: smart pointer (aka example of ownership ideo) doesnt have arithmetic, why?Larousse
R
4

tl;dr: Not really.

Pointers don't have any special move semantics. x = std::move(my_char_ptr) is the same as x = my_char_ptr. They are not similar in that regard to, say, std::vector's, in which moving takes away the allocated space.

However, in your case, if you want to keep existing heap buffers and treat them as strings - it can't be using std::string's, as they can't be constructed as a wrapper of an existing buffer (and there's small-string optimization etc.). Instead, consider either implementing a custom container, e.g. with some string data buffer (std::vector<char>) and an std::vector<std::string_view>, whose elements point into that buffer.

Remuneration answered 3/6, 2018 at 12:36 Comment(1)
@SombreroChicken: ?Remuneration

© 2022 - 2024 — McMap. All rights reserved.