如何知道 shared_ptr 是否已经序列化以提升存档

How to know if shared_ptr was already serialized to boost archive

在我的程序中,我有 c++ class 对象保留 SmartPointers 成员(SmartPointer 是我自己的自定义 class 派生自 boost::shared_ptr)。按照设计,我的某些 class 对象必须保留唯一的 SmartPtr,即不允许共享所有权。

出于调试的原因,我想实现检查模块,它将测试给定的 c++ class 对象(c++ 对象本身可以包含嵌套的 c++ class 对象成员)是否保持共享的智能指针所有权。如果是,我想抛出异常。我正在考虑为此目的重用序列化库,因为我已经为所有 classes 提供了序列化函数。我所要做的就是在我的 SmartPointer::serialize 函数中添加检查代码来测试指针是否已经保存在存档中。所以任何人都知道 boost 序列化中的这种功能会告诉我指针对象是否已经序列化?我认为 boost 序列化库必须有机制来检查这一点,因为在存档输出中,共享所有权的 shared_ptrs 只写了一次。

代码味道正在使用 shared_ptr,其中必须保证唯一所有权。发现违规后怎么办?断言? std::terminate?

您可以制作一个序列化包装器,添加对反序列化的检查。

您将取决于库的实现细节,因为 - 显然 - 在加载时会有 shared_ptr:

的临时副本

Live On Coliru

#undef NDEBUG
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/wrapper.hpp>
#include <iostream>

template <typename SP> struct Unique {
    SP& _ref;
    Unique(SP& r):_ref(r){}

    template <typename Ar>
    void serialize(Ar& ar, unsigned) {
        if constexpr (typename Ar::is_saving{}) {
            if (_ref && not _ref.unique())
                throw std::logic_error("Shared ownership not allowed");
        }
        ar & _ref;
        if constexpr (typename Ar::is_loading{}) {
            if (_ref && _ref.use_count() != 2)
                throw std::logic_error("Shared ownership not allowed");
        }
    }
};

template <typename SP>
struct boost::serialization::is_wrapper<Unique<SP>> : boost::mpl::true_ {};

struct Data {
    int a,b,c;
    void serialize(auto& ar, unsigned) { ar & a & b & c; }
};

static std::string do_write(auto const&... data) {
    std::ostringstream            oss;
    boost::archive::text_oarchive oa(oss);
    (oa << ... << data);
    return oss.str();
}

static void do_read(std::string const& str, auto&&... data)
{
    std::istringstream            iss(str);
    boost::archive::text_iarchive ia(iss);
    (ia >> ... >> data);
}

int main()
{
    auto data = std::make_shared<Data>(1, 2, 3);
    assert(data.unique());

    // okay:
    {
        auto txt = do_write(data, data, data);
        do_read(txt, data);
        std::cout << "L:" << __LINE__ << " Success " << std::endl;
    }

    // still okay:
    {
        Unique ud{data};
        auto txt = do_write(ud);
        do_read(txt, ud);
        std::cout << "L:" << __LINE__ << " Success " << std::endl;
        assert(data.unique());
    }

    // not okay because not unique:
    try
    {
        auto data_shared(data);
        Unique ud{data}, ud_shared{data_shared};
        assert(!data.unique());
        assert(!data_shared.unique());

        auto txt = do_write(ud, ud_shared);
        do_read(txt, ud, ud_shared);
        std::cout << "L:" << __LINE__ << " Success " << std::endl;
    }catch(std::exception const& e) {
        std::cout << "L:" << __LINE__ << " Failure " << e.what() << std::endl;
    }
}

版画

L:57 Success 
L:65 Success 
L:81 Failure Shared ownership not allowed

总结

不要这样做,因为你不能干净地做到这一点。另一种选择更糟糕(如一些评论者所建议的那样深入研究对象跟踪实现细节)。

简单

  • 收紧类型约束以表达您的不变性
  • 也许添加一个验证成员来检查不变量