Reputation: 519
I have a C function in an external library which takes a char**
argument (a pointer to a list of pointers, each of which represents the first character of a nul-terminated C-string).
At the client level, my list of strings is a C++ nested std::array scheme on the stack of type:
std::array<std::array<char, MAX_LEN>, NUM_STRINGS>
Question: What is the most elegant way to pass the nested std::array
contents to the C function which takes the char**
?
For a 1D std::array<T>
, we have the .data()
member which provides the T*
.
Is there an analogous way to get a T**
from a 2D std::array<std::array<T>>
which doesn't involve allocating a new array of type char*[NUM_STRINGS]
and copying each of the pointers to each inner element in a for loop (which is my current, but seemingly rather ugly and inefficient approach).
Upvotes: 2
Views: 89
Reputation: 4299
You have to create an array of pointers of size NUM_STRINGS that point to the start of each row of chars in order to use char **
indexing.
In the following example an array of pointers to chars is initialized by the function apc()
which takes the 2D char array as an argument:
#include <array>
#include <iostream>
const int NUM_STRINGS{3}, MAX_LEN{2};
std::array<char*, NUM_STRINGS> apc(std::array<std::array<char, MAX_LEN>, NUM_STRINGS> &a2d) {
std::array<char*, NUM_STRINGS> ret;
for (int i = 0; i < NUM_STRINGS; i++)
ret[i] = &a2d[i][0];
return ret;
}
std::array<std::array<char, MAX_LEN>, NUM_STRINGS> c2d{ {{'a','b'},{'c','d'},{'e','f'}} };
std::array<char*, NUM_STRINGS> pac = apc(c2d);
char** ppc = &pac[0];
int main()
{
for (int i = 0; i < NUM_STRINGS; i++)
std::cout << ppc[i][0] << ppc[i][1] << '\n';
}
/* outputs:
ab
cd
ef
*/
Upvotes: 1
Reputation: 13076
Something I almost always do when switching from one technology (language/rpc) to another is to make sure I have "adapters" (classes or functions). So that in the C++ I can keep using the full C++ benefits and only adapt at the last moment to the other language (in this case "C").
For your case that would look like this :
#include <array>
#include <iostream>
void legacy_func(char** strings, size_t rows, size_t columns)
{
for(size_t row = 0; row < rows; ++row)
{
for(size_t column = 0; column < columns; ++column )
{
std::cout << strings[row][column];
}
std::cout << "\n";
}
}
// You will need a small helper function to make
// the "C" style pointer to pointers
template<std::size_t rows_v, std::size_t cols_v>
void func(std::array<std::array<char,cols_v>,rows_v>& strings)
{
std::array<char*,cols_v> char_ptrs{};
for(std::size_t row = 0; row < rows_v; ++row)
{
char_ptrs[row] = strings[row].data();
}
// and now you can call the legacy function
legacy_func(char_ptrs.data(),rows_v,cols_v);
}
int main()
{
std::array<std::array<char,3>,2> strings{{{'1','2','3'},{'4','5','6'}}};
func(strings);
}
Upvotes: 2