Chiel
Chiel

Reputation: 6194

Passing std::vector<bool> to an external function

I am dealing with a library of which I cannot change the data types. I need to call a function that takes a bool array. The code that I am working with uses std::vector<bool> to store the bools. I have read a lot about std::vector<bool> and the associated problems on SO, but I have not found the most elegant solution for my problem. This is a code where performance is crucial. How do I deal with this problem in the most efficient way? I can change the type stored in the std::vector, but I cannot escape from using std::vector.

In my own problem, I have to call Fortran functions, but I have made a minimal example in C++ that illustrates the problem.

#include <cstdio>
#include <vector>

void print_c_bool(bool* bool_array, size_t n)
{
    for (int i=0; i<n; ++i)
        printf("%d, %d\n", i, bool_array[i]);
}

int main()
{
    // This works.
    bool bool_array[5] = {true, false, false, true, false};
    print_c_bool(bool_array, 5);

    // This is impossible, but how to solve it?
    std::vector<bool> bool_vector {true, false, false, true, false};
    // print_c_bool(bool_vector.data(), bool_vector.size());

    return 0;
}

Upvotes: 2

Views: 770

Answers (3)

Fareanor
Fareanor

Reputation: 6805

I tried to make it work using your example and the following (quite dirty) solution worked fine:

#include <iostream>
#include <vector>

void test_print(bool * arr, size_t s)
{
    for(unsigned int i = 0; i< s; ++i)
        std::cout << i << ": " << (arr[i]?"true\n":"false\n");
    std::cout << std::endl;
}

int main()
{
    bool b_arr[5] = {true, false, false, true, false};
    test_print(b_arr, 5);

    //std::vector <uint8_t> b_vec = {true, false, false, true, false}; // former proposal
    std::vector <char> b_vec = {true, false, false, true, false}; // new proposal
    test_print(reinterpret_cast<bool*>(b_vec.data()), b_vec.size());

    return 0;
}

The output I got is:

0: true
1: false
2: false
3: true
4: false

0: true
1: false
2: false
3: true
4: false

However, I don't know if it will behave the same on every system/platform. But if you don't intend to change your system/platform and if it does work on yours, I think it could do the job even if it is a quite dirty solution.

In fact, if we can guarantee that sizeof(bool) == sizeof(uint8_t) and assuming that true and false are respectively treated in memory as integers 1 and 0, this solution will work, but not otherwise.

I hope it can help.

EDIT: Replaced uint8_t by char type.


EDIT2:

Another solution that does not violate the strict aliasing rule is the one that Kamil Cuk mentioned that is to create a Bool wrapper and still check that sizeof(bool) == sizeof(Bool).

A possible implementation (just the baseline code) can be:

struct Bool
{
    bool v;

    Bool(bool bv=false) : v(bv)
    {}
};

And then you will be able to write:

std::vector<Bool> b_vec {true, false, false, true, false};
test_print(reinterpret_cast<bool*>(b_vec.data()), b_vec.size());

It worked for me :)

Upvotes: 0

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

I can change the type stored in the std::vector, but I cannot escape from using std::vector.

In this case you are out of luck. std::vector<bool> does not store an array of booleans, neither does its data() return a pointer to an array of booleans (as all other std::vectors do for their respective data type).

You can play some tricks and use std::vector<uint8_t> or similar, though even if the size matches, a uint8_t* is not a bool* ! Given that the function cannot change, you can only avoid to violate strict aliasing by copying the data into a bool array.

If you do care about performance, then I'd suggest to consider not using std::vector<bool>. For example, do you really need dynamic size? If not use a std::array.

Upvotes: 2

KamilCuk
KamilCuk

Reputation: 140880

You know what you have to do... Create a temporary array of bools and copy the values.

auto v = new bool[bool_vector.size()];
std::copy(bool_vector.begin(), bool_vector.end(), v);
print_c_bool(v, bool_vector.size());
delete[] v;

or

auto v = std::make_unique<bool>(bool_vector.size());
std::copy(bool_vector.begin(), bool_vector.end(), v.get());
print_c_bool(v.get(), bool_vector.size());

Upvotes: 5

Related Questions