NKatUT
NKatUT

Reputation: 519

Pass nested std::array<std::array<char,M>,N> to C function taking a char**

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

Answers (2)

doug
doug

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

Pepijn Kramer
Pepijn Kramer

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

Related Questions