Constant character pointer using unique_ptr

2.5k Views Asked by At

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?

3

There are 3 best solutions below

5
NathanOliver On BEST ANSWER

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**
8
Michael Kenzel On

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…

2
Richard Hodges On

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());
}