0

Is it possible to convert std::vector<std::string> to char* [] in one line?

Assume that I have this:

std::vector<std::string> my_vector;

And I add lots of text to it:

my_vector.push_back("A");
my_vector.push_back("AB");
my_vector.push_back("ABC");

Then I want to create a char * [] array from my_vector. How can that be done? Is there any smart functionality in C++ that can do this?

The goal is to have:

const char * my_array_cstr[] = {"A", "AB", "ABC"};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
euraad
  • 2,467
  • 5
  • 30
  • 51
  • 1
    Sure, just use `std::transform`. But you do need to declare the sufficiently-sized array first. It can't be created ad-hoc, in this manner, C++ does not work like that, on a fundamental level. A `std::transform`, that populates an existing array, is the most that can be done here. – Sam Varshavchik Jun 06 '23 at 16:04
  • 3
    What is wrong with `c_str()`? – Marek R Jun 06 '23 at 16:12
  • Do you really want to create an array of pointers to individual characters in the string? That is possible, just know that if the string content changes (in size) all your pointers will be invalid. If not do you mean `char*` (char[] is kind of the same) – Pepijn Kramer Jun 06 '23 at 16:30
  • Now the big question is why do you want to do this? The only reason I can see is that you are interacting with an old style api. – Pepijn Kramer Jun 06 '23 at 16:43
  • Related/dupe: [How to pass a vector of strings to execv](https://stackoverflow.com/questions/5797837/) – Remy Lebeau Jun 06 '23 at 17:50

3 Answers3

2

Sort of.

You can create something that (at least inside of the C++ domain) looks and acts like an array of pointers in one line. Since you haven't told us what you're hoping to accomplish by this, it's an open question whether it suits your purposes or not.

#include <vector>
#include <string>
#include <ranges>

int main() {
    // some input to process
    std::vector<std::string> input{ "A", "AB", "ABC"};

    // do the transform in one line
    auto ptrs = input | std::views::transform([](auto &s) { return s.c_str(); });

    // use our "array of pointers":
    for (auto i : ptrs)
        printf("%s\n", i);
}

In theory, if you need something that's more like a real array of pointers (e.g., you can pass its address to a C function that expects the address of the beginning of an array) you can use a C++23 feature to do that:

auto ptrs = input | std::views::transform([](auto &s) { return s.c_str(); }) | to<std::vector>();

In this case, ptrs will be an actual vector of pointers, so its .data() will give you an actual pointer to a contiguous, dynamically allocated block of memory filled with pointers.

As I write this (6 June, 2023) there's only one compiler that actually implements that though: Microsoft C/C++ 19.34 (or, presumably newer). But g++ and Clang are still both missing this, so depending on your target, it may be no problem at all, or it may be completely unacceptable.

If you need to go the next step after that, and create a real, actual array...then you're largely out of luck. Although some compilers don't enforce the requirement, C++ requires that the size of an array be specified as a compile-time constant, so there's no way to create one with the same number of elements as an arbitrary vector (though if you know a maximum size at compile time, you can create an array of that maximum size and just use it).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

It's not possible to do that in one line, or rather in one expression. In general there shouldn't be a reason to convert a vector to an array, unless you want compatibility with some c library. But, considering that a vector is a contiguous storage type you can do this:

/* My vector */
std::vector<std::string> my_vector { "A", "AB", "ABC" };

/* Fill a temporary vector with `const char*`s */
std::vector<const char*> temp;
temp.reserve(my_vector.size());
for (auto& s : my_vector)
    temp.push_back(s.c_str());

/* Get `temp`'s array */
const char** my_array = temp.data();

If the contents of my_vector are all string literals, you can use std::vector<const char*> from the start and call data() to get the array pointer if necessary.

guard3
  • 823
  • 4
  • 13
  • 1
    Please put some warnings on this, that if you return `my_array` that the memory is gone. And if you change `temp`, there's a probability that it will be gone, etc. ALL the warnings on this one. It _should_ work if you call the consumer from this scope immediately, but thar be dragons if you do anything else. – Kevin Anderson Jun 06 '23 at 16:39
-2

Is it possible to convert std::string to char* [] in one line inside C++?

None that I know of.

You can allocate a block of memory according to the size of the array. Then initialize it to the desired values via a for loop in a separate statement.

One (not recommended) option to allocate the memory is new/delete[]:

const auto my_size = my_vector.size();
const char **my_array_cstr = new const char *[my_size];
for (int i = 0; i < my_size; ++i) my_array_cstr[i] = my_vector[i].c_str();
// Use my_array_cstr
delete[] my_array_cstr;

Another is a variable length array, but this is not standard and can only be used if your compiler supports it:

const auto my_size = my_vector.size();
const char *my_array_cstr[my_size];
for (int i = 0; i < my_size; ++i) my_array_cstr[i] = my_vector[i].c_str();

Depending on your use case, if the size of the vector will be known at compile time, you can use a fixed size stack array:

const constexpr auto my_size = 3;
const char *my_array_cstr[my_size];
for (int i = 0; i < my_size; ++i) my_array_cstr[i] = my_vector[i].c_str();
user16217248
  • 3,119
  • 19
  • 19
  • 37
  • 1
    You really should use `std::vector` instead of `new[]`/`delete[]`, eg: `std::vector my_array_cstr; my_array_cstr.reserve(my_vector.size()); for (auto &s : my_vector) { my_array_cstr.push_back(s.c_str()); } // Use my_array_cstr.data() as needed...` – Remy Lebeau Jun 06 '23 at 17:51
  • @RemyLebeau Why is that better than `new`/`delete[]`? Both allocate on the heap anyway. P.S. I amended my answer. – user16217248 Jun 06 '23 at 18:17
  • 1
    @user16217248 `std::vector` manages the allocated memory for you, and ensures it gets destroyed properly, even if an exception is thrown. Using `new[]`/`delete[]` is more error-prone when used incorrectly (ie, mismatching `new`/delete[]` and `new[]`/`delete`, memory leaks, etc), and generally should be avoided in modern coding practices. Let the compiler/standard library handle the complexities for you – Remy Lebeau Jun 06 '23 at 18:38