4

I need to call the getList function below which comes from a library I cannot change.

#include <iostream>
#include <vector>
#include <string>

//function already exists in a separate library, can't be changed
void getList(const char* list[], int count){};

int main()
{
    std::vector<std::string> vectorOfStrings = {"123" , "abc", "def", "456"};
    
    //call getList and pass vectorOfStrings and vectorOfStrings.size()
    getList(/*convert std::vector<std::string> to const char** */, vectorOfStrings.size());
    
    return 0;
}

I already asked a similar question here and got an answer but I thought there might be a C++ way of doing this.

273K
  • 29,503
  • 10
  • 41
  • 64
A.Agan
  • 103
  • 6

2 Answers2

5

Use a std::vector<const char*> to store the pointers of the string data stored in the original std::vector<std::string>.

This is a fast operation because you're copying the original data but mind that: this requires that the const char* passed to the function are not stored anywhere because they'll become INVALID once the original string is deallocated (you'll get undefined behavior).

std::vector<std::string> strings = { "foo", "bar", "baz" };
std::vector<const char*> cstrings;
cstrings.reserve(strings.size());

std::transform(
    strings.begin(), 
    strings.end(), 
    std::back_inserter(cstrings), 
    [] (const auto& string) { return string.c_str();}
);

getList(cstrings.data(), cstrings.size());
Jack
  • 131,802
  • 30
  • 241
  • 343
  • I like the use of stl. Th syntax is a bit nicer with`std::ranges::transform` in C++20/C++23 – joergbrech Dec 15 '22 at 16:05
  • Prior to C++23, you can still simplify this a little by removing the `back_inserter`, eg: `std::vector cstrings(strings.size()); std::transform(strings.begin(), strings.end(), cstrings.begin(), [] (const auto& string) { return string.c_str(); } );` – Remy Lebeau Dec 15 '22 at 17:50
3

Given that the vector elements are not a C type, you'll need to construct an array to pass to the function:

const char **a = new const char *[vectorOfStrings.size()];
for (int i=0;i<vectorOfStrings.size();i++) {
    a[i] = vectorOfStrings[i].c_str();
}
getList(a, vectorOfStrings.size());
delete[] a;

Or, in a more C++ way:

std::vector<const char *> v2;
for (int i=0;i<vectorOfStrings.size();i++) {
    v2.push_back(vectorOfStrings[i].c_str());
}
getList(v2.data(), vectorOfStrings.size());
dbush
  • 205,898
  • 23
  • 218
  • 273
  • It will gove `const char *`, I believe. IMO `.data()` will be better – 0___________ Dec 15 '22 at 15:25
  • @EricPostpischil since C++11, `string.c_str()` and `string.data()` are required to return a pointer to the same null-terminated memory. The difference being that `.c_str()` is a read-only pointer, whereas `.data()` is read-only up to C++17 and then it is read/write pointer. – Remy Lebeau Dec 15 '22 at 17:44