【问题标题】:Boost Fusion container of shared pointers (shared_ptr) causing Segmentation Fault (sigsegv) or garbage resultsBoost Fusion 共享指针 (shared_ptr) 容器导致分段错误 (sigsegv) 或垃圾结果
【发布时间】:2010-10-13 17:12:58
【问题描述】:

编辑:事实证明这是临时工的问题。基本上,我无知地使用 C++,就好像它像 Java 或 C# 一样工作,但事实并非如此。希望这将是一个很好的警示故事。

编辑:这个问题似乎只发生在joint_viewshared_ptr 的组合中。原始指针似乎在相同的场景中工作得很好,就像在一个普通的融合容器中的共享指针一样,它一次构建了它的所有项目,而不向它添加任何东西。详情如下:

我正在使用 mingw gcc 4.5.1 使用 boost fusion 容器并取回内容时遇到一个特殊问题。我有一个自定义类,它被包裹在std::shared_ptr 中,然后交给融合make_list()(或make_vector(),似乎无关紧要)。如果我可以一次将所有对象放入容器中,那一切都很好。当我向容器添加另一个共享指针时,问题似乎出现了,它产生了joint_view。我使用fusion::for_each() 进行迭代并传入一个函数对象以打印出该值。如果我正在迭代共享指针的普通融合容器,而不是 joint_view 或其中没有共享指针的 joint_view,它可以正常工作,但否则会出现分段错误或垃圾值。

以下是我为尝试隔离问题而编写的测试程序。关于问题可能是什么的任何想法?完全有可能我只是错过了我应该/不应该做的事情:(

#include <iostream>
#include <memory>

//BOOST SMART POINTERS
//I only use boost's shared_ptr for ONE test, results are the same.
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

//BOOST FUSION
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/container/generation/make_list.hpp>
#include <boost/fusion/include/make_list.hpp>
#include <boost/fusion/container/generation/make_vector.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/algorithm/transformation/push_back.hpp>
#include <boost/fusion/include/push_back.hpp>

using namespace std;
using namespace boost::fusion;

struct TestStructInt {
    int test_int;

    TestStructInt(int init_num) : test_int(init_num) {};
};

struct TestStructString {
    string test_string;

    TestStructString(string init_str) : test_string(init_str) {};
};

struct do_junk{
    void operator()(string t) const {
        cout << t << endl;
    }

    void operator()(string* t) const {
        cout << *t << endl;
    }

    void operator()(std::shared_ptr<int> t) const {
        cout << *t << endl;
    }

    void operator()(std::shared_ptr<string> t) const {
        cout << *t << endl;
    }

    void operator() (boost::shared_ptr<string> t) const {
        cout << *t << endl;
    }

    void operator() (TestStructInt t) const {
        cout << t.test_int << endl;
    }

    void operator() (std::shared_ptr<TestStructInt> t) const {
        cout << t->test_int << endl;
    }

    void operator() (TestStructString t) const {
        cout << t.test_string << endl;
    }

    void operator() (std::shared_ptr<TestStructString> t) const {
        cout << t->test_string << endl;
    }

    void operator() (std::shared_ptr<TestStructString*> t) const {
        cout << (*t)->test_string << endl;
    }
};

int main()
{
    string foo1 = "foo1";
    string foo2 = "foo2";
    string foo3 = "foo3";
    int bar1 = 1;
    int bar2 = 2;
    int bar3 = 3;
    string* foo1_ptr = &foo1;
    string* foo2_ptr = &foo2;
    string* foo3_ptr = &foo3;
    auto foo1_obj = make_shared<TestStructString>(TestStructString("foo1"));
    auto foo2_obj = make_shared<TestStructString>(TestStructString("foo2"));
    auto foo3_obj = make_shared<TestStructString>(TestStructString("foo3"));

    //works fine
    auto list_test1 = make_list(foo1, foo2);

    //works fine
    auto list_test2 = make_list(foo1_ptr, foo2_ptr);

    //seems to work, but is undefined behavior
    auto list_test3
        = make_list(
            std::make_shared<int>(bar1), std::make_shared<int>(bar2)
        )
    ;

    //seems to work, but is undefined behavior
    auto list_test4
        = make_list(
            std::make_shared<string>(foo1), std::make_shared<string>(foo2)
        )
    ;

    //seems to work, but is undefined behavior
    auto list_test5
        = make_list(
            std::make_shared<TestStructInt>(TestStructInt(1))
            , std::make_shared<TestStructInt>(TestStructInt(2))
        )
    ;

    //seems to work, but is undefined behavior
    auto list_test6
        = make_list(
            std::make_shared<TestStructString>(TestStructString("foo1"))
            , std::make_shared<TestStructString>(TestStructString("foo2"))
        )
    ;

    //seems to work, but is undefined behavior
    auto list_test7
        = make_list(TestStructString("foo1"), TestStructString("foo2"))
    ;

    //seems to work, but is undefined behavior
    auto joint_view_test1 = push_back(make_list(foo1, foo2), foo3);

    //seems to work, but is undefined behavior
    auto joint_view_test2 = push_back(make_list(foo1_ptr, foo2_ptr), foo3_ptr);

    //seems to work, but is undefined behavior
    auto joint_view_test3
        = push_back(
            make_list(
                TestStructString(foo1), TestStructString(foo2)
            )
            , TestStructString(foo3)
        )
    ;

    //integer values I pass in are coming out different
    auto joint_view_test4
        = push_back(
            make_list(
                std::make_shared<int>(bar1), std::make_shared<int>(bar2)
            )
            , make_shared<int>(bar3)
        )
    ;

    //pass in foo1, foo2, and foo3, but only get foo3's value back out for each
    auto joint_view_test5
        = push_back(
            make_list(
                std::make_shared<string>(foo1), std::make_shared<string>(foo2)
            )
            , make_shared<string>(foo3)
        )
    ;

    //causes seg fault when running do_junk()
    auto joint_view_test6
        = push_back(
            make_vector(
                std::make_shared<string>(foo1), std::make_shared<string>(foo2)
            )
            , std::make_shared<string>(foo3)
        )
    ;

    //causes seg fault when running do_junk()
    auto joint_view_test7
        = push_back(
            make_list(
                boost::make_shared<string>(foo1)
                , boost::make_shared<string>(foo2)
            )
            , boost::make_shared<string>(foo3)
        )
    ;

    //integer values I pass in are coming out different
    auto joint_view_test8
        = push_back(
            make_list(
                std::make_shared<TestStructInt>(TestStructInt(1))
                , std::make_shared<TestStructInt>(TestStructInt(2))
            )
            , std::make_shared<TestStructInt>(TestStructInt(3))
        )
    ;

    //causes seg fault when running do_junk()
    auto joint_view_test9
        = push_back(
            make_list(
                std::make_shared<TestStructString>(TestStructString("foo1"))
                , std::make_shared<TestStructString>(TestStructString("foo2"))
            )
            , std::make_shared<TestStructString>(TestStructString("foo3"))
        )
    ;

    //causes seg fault when running do_junk()
    auto joint_view_test10
        = push_back(
            make_list(
                std::make_shared<TestStructString*>(new TestStructString("foo1"))
                , std::make_shared<TestStructString*>(new TestStructString("foo2"))
            )
            , std::make_shared<TestStructString*>(new TestStructString("foo3"))
        )
    ;

    //seems to work, but is undefined behavior
    auto joint_view_test11
        = push_back(
            make_list(
                foo1_obj
                , foo2_obj
            )
            , foo3_obj
        )
    ;

    cout << "@@ list1" << endl;
    boost::fusion::for_each(list_test1, do_junk());
    cout << "@@ list2" << endl;
    boost::fusion::for_each(list_test2, do_junk());
    cout << "@@ list3" << endl;
    boost::fusion::for_each(list_test3, do_junk());
    cout << "@@ list4" << endl;
    boost::fusion::for_each(list_test4, do_junk());
    cout << "@@ list5" << endl;
    boost::fusion::for_each(list_test5, do_junk());
    cout << "@@ list6" << endl;
    boost::fusion::for_each(list_test6, do_junk());
    cout << "@@ list7" << endl;
    boost::fusion::for_each(list_test7, do_junk());
    cout << "@@ joint_view1" << endl;
    boost::fusion::for_each(joint_view_test1, do_junk());
    cout << "@@ joint_view2" << endl;
    boost::fusion::for_each(joint_view_test2, do_junk());
    cout << "@@ joint_view3" << endl;
    boost::fusion::for_each(joint_view_test3, do_junk());
    cout << "@@ joint_view4" << endl;
    boost::fusion::for_each(joint_view_test4, do_junk());
    cout << "@@ joint_view5" << endl;
    //boost::fusion::for_each(joint_view_test5, do_junk());
    cout << "@@ joint_view6" << endl;
    //boost::fusion::for_each(joint_view_test6, do_junk());
    cout << "@@ joint_view7" << endl;
    //boost::fusion::for_each(joint_view_test7, do_junk());
    cout << "@@ joint_view8" << endl;
    //boost::fusion::for_each(joint_view_test8, do_junk());
    cout << "@@ joint_view9" << endl;
    //boost::fusion::for_each(joint_view_test9, do_junk());
    cout << "@@ joint_view10" << endl;
    //boost::fusion::for_each(joint_view_test10, do_junk());
    cout << "@@ joint_view11" << endl;
    boost::fusion::for_each(joint_view_test11, do_junk());
    cout << "@@" << endl;

    return 0;
}

【问题讨论】:

    标签: c++ boost segmentation-fault boost-fusion


    【解决方案1】:

    fusion::joint_view 保留它的元素作为参考。您需要非常小心,它不会引用临时对象(在您的设置中,它们会在迭代时超出范围)。您的示例中似乎就是这种情况。

    【讨论】:

    • 对,我了解joint_view 保存其元素的方法,并认为我可能会丢失临时对象,但这就是为什么我有这么多测试使用'shared_ptr' 来包装进入容器的项目。同样,如果我一次将所有项目都放入容器中,并且不使用“push_back()”添加任何内容,它就可以正常工作。另外,如果您查看joint_view_test3,我没有使用“shared_ptr”,但我正在添加另一个项目并且它工作正常。当我有一个“shared_ptr”容器并向其中添加另一个容器,然后将生成的joint_view 提供给“for_each()”时,我遇到了问题。
    • 你的意思是什么?对我来说,很明显您正在创建临时对象,这些临时对象绑定到joint_view 中的引用。不要尝试存储joint_view,而是直接将其传递给应该对其进行处理的函数。这将防止临时人员过早超出范围。如果您不确定编译器使临时变量保持多长时间,请阅读标准(提示:通常直到下一个分号,但这只是一个粗略的估计)。
    • 仔细听了你所说的...我添加了另一个测试用例并且它有效,我理解它为什么有效,但我仍然有点迷茫,所以请多多包涵。是的,我正在创建临时文件,但将它们包装在智能指针中,并将它们提供给融合容器,它本身就是一个临时文件。但是在容器超出范围之前,我向它添加了另一个 ptr-wrapped temp,生成了一个 joint_view ,它将引用保存到...我的智能指针(我的假设)?还是它对我的智能指针后面的元素有引用?如果它剥离了我的指针,那么我理解我的对象超出了范围。
    • @pheadbaq:引用参数的生命周期是多少?一旦函数作为返回参数,该生命周期就会结束。因此,在这种情况下,“引用的生命周期”在返回 joint_view 时已经结束。但是临时仍然存在,因为它包含的完整表达式尚未结束。在完整表达式的评估结束后很短的时间,破坏了临时的,后来for_each 被调用,它试图访问一个死对象,导致未定义的行为。
    • @pheadbaq:是的,你现在完全明白了,这正是我想要交流的。一个小提示:不仅临时列表消失了,使用make_shared 创建的临时shared_ptr 也消失了(第二个参数到push_back)。此外,出于同样的原因,您的第一个 push_back 示例(TestStructString(fooN) 也包含 UB(尽管看起来工作正常)。对于最后一个 push_back 示例也可以这样说(由于临时列表)。
    【解决方案2】:

    我不能从示例中 100% 确定这是您正在做的事情,但您通常不能在迭代列表时更改列表的内容,否则您的迭代器无效。将您添加的项目列表排入队列,然后在最后一次性添加所有项目。

    【讨论】:

    • 好吧,我在迭代过程中没有改变任何东西,只是打印 Fusion 容器中应该存在的值,所以我认为不会是这样。根据 Fusion 的文档(如果我没看错的话),执行 push_back 不会改变 Fusion contianer。相反,Fusion 创建了一个指向 Fusion 容器和您“添加”到其中的任何新对象的视图,并且 for_each 为您进行迭代,将容器中的每个项目传递给您提供的函数对象。
    • 另外,排队不同类型的对象正是我使用 Fusion 容器的原因。但由于这个问题,我似乎无法取回我输入的内容。
    猜你喜欢
    • 2011-06-07
    • 2021-09-09
    • 1970-01-01
    • 2018-06-17
    • 2022-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    相关资源
    最近更新 更多