4

Let's say that I have a vector<string> input and I need to pass this to a function that takes a const char** argument. My thought had been to use a unique_ptr like this:

const auto output = make_unique<char*>(size(input)); 

But I can't seem to turn a const unique_ptr<const*> into a const char**. Is there a way to accomplish this, or perhaps a simpler alternative?

9
  • @Ron Well... unique_ptr is the name of a standard object, so it seemed right to preserve it's capitalization... Commented Jul 16, 2018 at 15:33
  • 4
    What is const* supposed to be? Commented Jul 16, 2018 at 15:35
  • 1
    Does the function take a char** so that it can change what the char* points to or is it expecting an array of strings? Commented Jul 16, 2018 at 15:35
  • @MilesBudnek The function is in a C-API. It's doing a deep copy from all the strings. So we're really talking about a const char* const* but for purposes of the question we should probably treat it like I didn't know that and it was actually asking for a const char*. Commented Jul 16, 2018 at 15:37
  • 1
    @JonathanMee I was so convinced you actually wrote the array overload that my brain completely shut off. Commented Jul 16, 2018 at 15:57

3 Answers 3

4

I would just build a vector of pointers to the c_str()'s of the strings and then get a pointer to that.

std:vector<const char*> pointers; pointers.reserve(input.size()); for (const auto& e : input) pointers.push_back(e.c_str()); // get const char *'s auto argument = pointers.data(); // get pointer to const char*'s - const char** 

Or using a unique_ptr

auto pointers = std::make_unique<const char*[]>(size(input)) for (size_t i = 0; i < input.size(); ++i) pointers[i]= input[i].c_str(); // get const char *'s auto argument = pointers.get(); // get pointer to const char*'s - const char** 
Sign up to request clarification or add additional context in comments.

5 Comments

@JonathanMee You could do auto pointers = std::unique_ptr<const char*[]>(new const char*[input.size()]); and then do the for loop.
@JonathanMee It's more that there's no good reason to bother with std::unique_ptr in this situation, since std::string is the correct ideomatic expression for storing owned Strings, and std::unique_ptr just adds additional code cruft.
@NathanOliver Right you are const auto output = make_unique<const char*[]>(size(input)) works fine.
I agree with Xirema. I have written code that does this with unique pointer while making old code exception safe, but the very first thing I did after that is to use vector.
@JonathanMee Blast, I forgot make_unique works with the array form. I just reached for std::vector as that is what I normally use. Wan't me to add the unique_ptr version to the answer?
3

I assume you need this to fit some interface you have no control of, otherwise I would consider adapting the interface in question to avoid unnecessary creation of temporary data just for the sake of fitting an ill-fitted interface…

Since you just need a temporary array of known size, the simplest solution would probably be to allocate an array of pointers and fill it with pointers to the strings in your vector:

auto pointers = std::make_unique<const char*[]>(size(v)); std::transform(begin(v), end(v), &pointers[0], [](const auto& s) { return s.c_str(); }); 

This array could also be placed on the stack to avoid dynamic memory allocation. But since you're working with strings here and are willing to copy data into a temporary array, I assume performance is not critical, so I guess there's no need for the added complexity…

8 Comments

I mean I think that transform(begin(v), end(v), pointers.get(), [](auto i){ return data(i); }) would be simpler, don't you? But yes this is the answer that I was looking for.
Gratuitous use of mutable lambdas. I don't like it.
Indeed, using std::transform here is much better. I somehow didn't think of that in the moment I wrote this. I'll update my answer.
@TimSeguine I mean it's either a lambda or a for-loop like in NathanOliver's answer. I personally prefer the lambda. But that's really just a style choice.
@Jonathan I have no problem with using a lambda. I have a problem with using a lambda with a mutable capture where none is needed. I prefer the transform version also to a for loop.
|
1

two approaches, depending on whether the c interface requires null termination or not:

#include <vector> #include <string> #include <algorithm> auto make_c_interface_null_terminated(std::vector<std::string> const &input) -> std::vector<const char*> { auto result = std::vector<const char*>(input.size() + 1); auto to_c_str = [](auto&& str) { return str.c_str(); }; std::transform(begin(input), end(input), begin(result), to_c_str); // implied: result[result.size() - 1] = nullptr return result; } auto make_c_interface(std::vector<std::string> const &input) -> std::vector<const char*> { auto result = std::vector<const char*>(input.size()); auto to_c_str = [](auto&& str) { return str.c_str(); }; std::transform(begin(input), end(input), begin(result), to_c_str); return result; } extern "C" void c_interface_requires_null(const char** argv); extern "C" void c_interface_sized(size_t size, const char** args); void test(std::vector<std::string> const &input) { auto output1 = make_c_interface_null_terminated(input); c_interface_requires_null(output1.data()); auto output2 = make_c_interface(input); c_interface_sized(output1.size(), output1.data()); } 

2 Comments

Is the auto&& the right choice for the parameter lambda? Since the str.c_str() has a life-span associated with the owning string (which had better not mutate or destruct in the interim).
@Eljay auto&& is always the correct type for a lambda's arguments. It uses the same type deduction process as template function argument type deduction. In this case it will deduce to std::string const& because input is a reference to const.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.