Reputation: 1999
I have a legacy function that has an input parameter of type const char* array
myfunc(const char* names[]);
I have a set std::set<std::string> mynames;
. The set has already been populated.
I have to pass mynames
to myfunc
Using range based loop, I have
const char* names[];
int i = 0;
for (const auto &name : mynames)
{
//allocate memory to names here
names[i] = name.c_str();
++i;
}
//call myfunc
myfunc(names);
Is there a better way to do this?
Upvotes: 0
Views: 1023
Reputation: 596497
You are not allocating any memory for the names[]
array. You need to do something more like this:
const char** names = new const char*[mynames.size()]; // +1 if needed
size_t i = 0;
for (const auto &name : mynames)
{
names[i++] = name.c_str();
}
/* if needed:
names[i] = nullptr;
*/
/* alternatively
std::transform(mynames.begin(), mynames.end(), names,
[](const std::string &name){ return name.c_str(); }
);
// if needed :
// names[mynames.size()] = nullptr;
*/
myfunc(names);
delete[] names;
Which can then be simplified further by using std::vector
instead of new[]
, eg:
std::vector<const char*> names;
names.reserve(mynames.size()); // +1 if needed
for (const auto &name : mynames)
{
names.push_back(name.c_str());
}
/* if needed:
names.push_back(nullptr);
*/
/* alternatively:
std::vector<const char*> names(mynames.size()); // +1 if needed
std::transform(mynames.begin(), mynames.end(), names.begin(),
[](const std::string &name){ return name.c_str(); }
);
// if needed:
// names.back() = nullptr;
*/
myfunc(names.data());
Upvotes: 2
Reputation: 5871
If you can rewrite this function, then do so. However, there are plenty of longstanding third party libraries like this, and you don't want to try to mess with them.
You are going to have to allocate a temporary data structure that provides the data in the format that function expects. You are going to have to figure out how long it has to exist. I'll show a sample here that assumes that you can skip allocating the individual strings, and just allocate one array for their pointers (as you have done).
You didn't allocate the space for the pointers, and you didn't add a nullptr at the end. Notice that the function takes a raw array, so it can't know how many names there are. It will almost certainly expect a NULL to signal the last name.
#include <iostream>
#include <string>
#include <set>
// This demonstrates how a legacy function like this might work
int myfunc(const char* names[]) {
if (names) {
int index = 0;
const char* name = names[index];
// Notice that this function cannot know how many names there are.
// The function documentation will almost certainly tell you
// to add a NULL as the last entry.
while (name) {
std::cout << name << std::endl;
name = names[++index];
}
}
return 0;
}
int main()
{
const char * legacy [] = {
"one",
"two",
"three",
nullptr,
};
std::cout << "legacy" << std::endl;
myfunc(legacy);
std::set<std::string> modern { "one", "two", "three" };
// this temp structure is valid as long as it is
// allocated, and original set doesn't change
// although you probably should allocate and clean up
// a copy of each string within this temp array.
const char ** temp = new const char* [modern.size()+1];
int index = 0;
for (const auto &name : modern) {
temp[index++] = name.data();
}
temp[index] = nullptr;
// if myfunc just uses them and exits, you're okay
// if it remembers any of the pointers for later, that would be unfortunate
std::cout << "using temp" << std::endl;
myfunc(temp);
// delete what we allocated. The pointers inside are owned by the set, so we don't care about that
delete [] temp;
return 0;
}
Upvotes: 2