【问题标题】:Passing std::vector<bool> to an external function将 std::vector<bool> 传递给外部函数
【发布时间】:2019-05-06 08:51:41
【问题描述】:

我正在处理一个无法更改数据类型的库。我需要调用一个带有bool 数组的函数。我正在使用的代码使用std::vector&lt;bool&gt; 来存储布尔值。我已经阅读了很多关于std::vector&lt;bool&gt; 和 SO 相关问题的信息,但我还没有找到最优雅的解决方案来解决我的问题。这是一个性能至关重要的代码。如何以最有效的方式处理这个问题?我可以更改存储在std::vector 中的类型,但我无法逃避使用std::vector

在我自己的问题中,我必须调用 Fortran 函数,但我用 C++ 制作了一个最小示例来说明问题。

#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;
}

【问题讨论】:

  • std::vector&lt;char&gt; 可用于保存0s 或1s,但我也希望看到更好的解决方法
  • 你必须构造一个布尔数组,这是唯一的方法。
  • 这个怎么样:std::vector&lt;char&gt; bool_vector{ 1, 0 }; print_c_bool((bool *)bool_vector.data(), bool_vector.size());?大概。
  • @vahancho 否。它违反了严格的别名规则,并且在sizeof(char) != sizeof(bool)和其他情况下不起作用。
  • 恐怕你必须接受std::vector&lt;bool&gt;::data不是布尔数组的事实,如果你需要一个,那么你必须使用std::vector&lt;bool&gt;以外的其他东西

标签: c++ boolean stdvector hpc


【解决方案1】:

你知道你必须做什么......创建一个临时的布尔数组并复制值。

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;

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

【讨论】:

  • 我想避免复制,因为这是一个很大的性能杀手。
【解决方案2】:

我可以更改std::vector中存储的类型,但我无法逃脱 使用std::vector

在这种情况下,您不走运。 std::vector&lt;bool&gt; 不存储布尔数组,它的data() 也不返回指向布尔数组的指针(正如所有其他 std::vectors 为它们各自的数据类型所做的那样)。

您可以玩一些技巧并使用std::vector&lt;uint8_t&gt; 或类似的,尽管即使大小匹配,uint8_t* 也不是bool*!鉴于函数无法更改,您只能通过将数据复制到 bool 数组来避免违反严格别名。

如果您确实关心性能,那么我建议您考虑不使用std::vector&lt;bool&gt;。例如,你真的需要动态尺寸吗?如果不使用std::array

【讨论】:

  • 不幸的是,我需要一个动态尺寸。 uint8_t 似乎工作得很好,但保证可以在任何地方工作?
  • @Chiel 它永远不能保证工作!您错过了“uint8_t* 不是bool*!”在预期 bool* 时传递 uint8_t* 似乎可行,但它违反了 strict aliasing。严格的别名规则是一个棘手的规则,因为天真地看起来很好的东西实际上不是,一个微小的编译器优化可以完全搞砸你的程序
【解决方案3】:

我尝试使用您的示例使其工作,并且以下(相当肮脏的)解决方案工作正常:

#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;
}

我得到的输出是:

0:是的
1:假
2:假
3:是的
4:假

0:是的
1:假
2:假
3:是的
4:错误

但是,我不知道它在每个系统/平台上的行为是否都相同。但是,如果您不打算更改您的系统/平台并且它确实适用于您的系统/平台,我认为即使它是一个非常肮脏的解决方案也可以完成这项工作。

实际上,如果我们可以保证sizeof(bool) == sizeof(uint8_t) 并假设truefalse 在内存中分别被视为整数10,则此解决方案将有效,但否则无效。

希望对你有帮助。

编辑:将 uint8_t 替换为 char 类型。


EDIT2:

另一个不违反严格别名规则的解决方案是 Kamil Cuk 提到的,即创建一个 Bool 包装器并仍然检查 sizeof(bool) == sizeof(Bool)

一个可能的实现(只是基线代码)可以是:

struct Bool
{
    bool v;

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

然后你就可以写了:

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

它对我有用:)

【讨论】:

  • 有趣。那么最大的悬而未决的问题是我们可以在多大程度上保证这一点。有什么想法吗?
  • uint8_t(当然如果定义了)将始终具有相同的定义大小。据我所知,这个问题更多的是关于bool 的大小。但我真的不知道在哪些情况下它是 1(8 位),在哪些情况下不是(这取决于我认为的平台)。
  • afaik booluint8_tstrict aliasing 不兼容,并且不保证看起来有效。
  • @user463035818 如果我们使用char而不是uint8_t,则不再违反严格的别名规则,char的大小保证为1。结果是一样的.感谢您注意到我的错误,我不知道。
  • afaik 例外是 char* 别名不同类型的指针。它不适用于别名为char* 的任何其他类型。它是您在代码中使用的后者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
  • 1970-01-01
  • 2011-01-10
  • 2013-01-13
相关资源
最近更新 更多