42

I have a vector<std::string> variable. I need to pass it onto a method which accepts char**as an input parameter.

how to do this ? If possible I need to pass a writable one.

Update 1: In a tool for creating a service method, i give parameters as std::vector, but it sets automatically the qualifier as &, which means my method definition generated by the tool will look as:

std::string SvcImpl::myMethodname ( const std::string par1, const std::vector< std::string >& par2, const std::vector< std::string >& par3 ) { } 

This method gets called automatically with values in the patameter passed. Now from inside this method I'm going to call a method in a dll in a lib folder which looks like:

int method_to_be_called(char* par1, char ** par2, char ** par3, void* pRetValue); 

for par1 --> I'm passing (char*)par1.c_str()

I need to know how to pass variables for par2 and par3 and for pRetValue. values for par2 and par3 are available in vector but the last parameter pRetValue is an output parameter that i need to return it as std::string.

sorry if i am very confusing or asking very basic questions.

10
  • 1
    then your operation should be on *writable, assuming **writable is accepting your char ** param. Commented Sep 25, 2014 at 6:34
  • 1
    I think you are going to need to produce a new std::vector<char*> to store the addresses of all your string data. Commented Sep 25, 2014 at 6:44
  • What exactly does this method you are passing it to do? Commented Sep 25, 2014 at 6:45
  • Do you really need to copy the contents of the strings into a newed array, or can you just use a pointer to the underlying data directly? Commented Sep 25, 2014 at 6:45
  • @juanchopanza The "underlying data" would be an array of string objects, you can't just iterate through them as if they were an array of c-strings. Commented Sep 25, 2014 at 6:48

4 Answers 4

40

It is possible to solve the problem without copying out all the std::strings as long as the function does not modify the passed in char**. Otherwise I can see no alternative but to copy out everything into a new char**` structure (see second example).

void old_func(char** carray, size_t size) { for(size_t i = 0; i < size; ++i) std::cout << carray[i] << '\n'; } int main() { std::vector<std::string> strings {"one", "two", "three"}; std::vector<char*> cstrings; cstrings.reserve(strings.size()); for(size_t i = 0; i < strings.size(); ++i) cstrings.push_back(const_cast<char*>(strings[i].c_str())); // Do not change any of the strings here as that will // invalidate the new data structure that relies on // the returned values from `c_str()` // // This is not an issue after C++11 as long as you don't // increase the length of a string (as that may cause reallocation) if(!cstrings.empty()) old_func(&cstrings[0], cstrings.size()); } 

EXAMPLE 2: If the function must modify the passed in data:

void old_func(char** carray, size_t size) { for(size_t i = 0; i < size; ++i) std::cout << carray[i] << '\n'; } int main() { { // pre C++11 std::vector<std::string> strings {"one", "two", "three"}; // guarantee contiguous, null terminated strings std::vector<std::vector<char>> vstrings; // pointers to rhose strings std::vector<char*> cstrings; vstrings.reserve(strings.size()); cstrings.reserve(strings.size()); for(size_t i = 0; i < strings.size(); ++i) { vstrings.emplace_back(strings[i].begin(), strings[i].end()); vstrings.back().push_back('\0'); cstrings.push_back(vstrings.back().data()); } old_func(cstrings.data(), cstrings.size()); } { // post C++11 std::vector<std::string> strings {"one", "two", "three"}; std::vector<char*> cstrings; cstrings.reserve(strings.size()); for(auto& s: strings) cstrings.push_back(&s[0]); old_func(cstrings.data(), cstrings.size()); } } 

NOTE: Revised to provide better code.

Sign up to request clarification or add additional context in comments.

4 Comments

Good, although in the second version I would use a vector or at least a unique_ptr to hold the pointer array, and some kind of scope_exit facility to delete the pointers inside. If any of the individual string allocations fail here, all previous ones plus the pointer array are leaked.
in the comment of your code, you wrote that "this is not an issue after C++11." can you please explain why?
@lefenzy I edited the comment as it was not entirely accurate. But before C++11 there is no guarantee that the pointer returned by c_str() points to the string's actual data (it may be a temporary buffer). After C++11 it is guaranteed to point to the strings actual data. It is only invalidated if you increase the length of the string.
Been looking forever for a good answer -- this is what I needed that nobody else mentioned: the c_str() function! Thank you!
25

Galik's answer has a number of safety issues. Here is how I would do it in Modern C++:

#include <iostream> #include <string> #include <vector> void old_func(char** carray, std::size_t size) { for(std::size_t i(0); i < size; ++i) std::cout << carray[i] << '\n'; } void other_old_func(const char** carray, std::size_t size) { for(std::size_t i(0); i < size; ++i) std::cout << carray[i] << '\n'; } int main() { { std::cout << "modifiable version\n"; std::vector<std::string> strings{"one", "two", "three"}; std::vector<char*> cstrings{}; for(auto& string : strings) cstrings.push_back(&string.front()); old_func(cstrings.data(), cstrings.size()); std::cout << "\n\n"; } { std::cout << "non-modifiable version\n"; std::vector<std::string> strings{"four", "five", "six"}; std::vector<const char*> cstrings{}; for(const auto& string : strings) cstrings.push_back(string.c_str()); other_old_func(cstrings.data(), cstrings.size()); std::cout << std::endl; } } 

No messy memory management or nasty const_casts.

Live on Coliru.

Outputs:

modifiable version one two three non-modifiable version four five six 

1 Comment

Just be aware if you are using &string.front that the documentation states: "This function shall not be called on empty strings." I believe in practice it will be the same as &string[0] and data and c_str but it might trigger debug warnings.
1

The top rated answers for this question expect you to pass in a size with your char** parameters. But in method_to_be_called() there is no way to pass in a size for par2 and par3 so these lists of c-style strings probably expect to be null terminated. In other words the last string (char*) in the list of strings (char **) needs to be a null pointer. This is a common paradigm in many c libraries.

int method_to_be_called(char* par1, char ** par2, char ** par3, void* pRetValue); 

The most expedient way around this is probably to go with a more c-style answer.

//directly create char** par2 std::vector<std::string> par2Vect{"one", "two", "three"}; char ** par2 = (char**) malloc( sizeof(char*)*(par2Vect.size() + 1) ); for(size_t i = 0; i < par2Vect.size(); ++i) { par2[i] = strdup(par2Vect[i].c_str()); } // set the last entry to null to signify the end of the list par2[par2Vect.size()] = nullptr; // call your library method_to_be_called(..., par2,...); // delete par2 for(size_t i = 0; i < par2Vect.size(); ++i) { // free memory for each c-style string free(par2[i]); } // free memory for outer char* array free(par2); 

1 Comment

memory allocated with strdup uses malloc and thus should be freed using free not delete
0

I believe this is rather easy and can be done without too much of complexity.

std::vector<std::string> vector = {"a", "std::vector", "of", "std::string"}; // Result char**. char** result = new char*[vector.size()]; for (int index = 0; index < vector.size(); index++) { result[index] = const_cast<char*>(vector[index].c_str()); } // Use the result. delete[] result; // Deallocate the memory from heap after usage. 

1 Comment

This is ultimately identical to the accepted answer, except with manual memory management. This doesn't add anything useful.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.