std::atomic 作为类成员:使用 boost/python.hpp 时使用已删除的函数错误

Posted

技术标签:

【中文标题】std::atomic 作为类成员:使用 boost/python.hpp 时使用已删除的函数错误【英文标题】:std::atomic as class member: use of deleted function error when using boost/python.hpp 【发布时间】:2020-07-29 04:52:07 【问题描述】:

其他看似相关的问题似乎可以通过直接初始化原子来解决:

std::atomic_uint32_t v10;

但是,当我将 std::atomic 变量作为类成员时,我仍然会收到“使用已删除函数”错误。我也只在使用 Boost Python 包装我的类以便它可以从 Python 调用时得到错误。我认为这是因为 Boost Python 应用了额外的编译规则,而不是 Boost Python 的一些问题。

AtomicTest.cpp

#include "AtomicTest.h"
#include <boost/python.hpp>
using namespace boost::python;

void AtomicTest::init()

    printf("Atomic test\n");



BOOST_PYTHON_MODULE(atomicTest)

    class_<AtomicTest>("AtomicTest")
    .def("init", &AtomicTest::init)
    ;
;

AtomicTest.h

#ifndef ATOMICTEST_ATOMICTEST_H
#define ATOMICTEST_ATOMICTEST_H

#include <atomic>

class AtomicTest

public:
    void init();
    std::atomic_uint32_t v10;
;


#endif //ATOMICTEST_ATOMICTEST_H

构建日志:

In file included from /usr/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0,
                 from /usr/include/boost/python/object/value_holder.hpp:46,
                 from /usr/include/boost/python/object/class_metadata.hpp:14,
                 from /usr/include/boost/python/class.hpp:23,
                 from /usr/include/boost/python.hpp:18,
                 from /home/tb/CLionProjects/atomicTest/AtomicTest.cpp:6:
/usr/include/boost/python/object/value_holder.hpp: In instantiation of ‘boost::python::objects::value_holder<Value>::value_holder(PyObject*, A0) [with A0 = boost::reference_wrapper<const AtomicTest>; Value = AtomicTest; PyObject = _object]’:
/usr/include/boost/python/object/make_instance.hpp:72:16:   required from ‘static Holder* boost::python::objects::make_instance<T, Holder>::construct(void*, PyObject*, boost::reference_wrapper<const T>) [with T = AtomicTest; Holder = boost::python::objects::value_holder<AtomicTest>; PyObject = _object]’
/usr/include/boost/python/object/make_instance.hpp:46:31:   required from ‘static PyObject* boost::python::objects::make_instance_impl<T, Holder, Derived>::execute(Arg&) [with Arg = const boost::reference_wrapper<const AtomicTest>; T = AtomicTest; Holder = boost::python::objects::value_holder<AtomicTest>; Derived = boost::python::objects::make_instance<AtomicTest, boost::python::objects::value_holder<AtomicTest> >; PyObject = _object]’
/usr/include/boost/python/object/class_wrapper.hpp:29:37:   required from ‘static PyObject* boost::python::objects::class_cref_wrapper<Src, MakeInstance>::convert(const Src&) [with Src = AtomicTest; MakeInstance = boost::python::objects::make_instance<AtomicTest, boost::python::objects::value_holder<AtomicTest> >; PyObject = _object]’
/usr/include/boost/python/converter/as_to_python_function.hpp:27:61:   required from ‘static PyObject* boost::python::converter::as_to_python_function<T, ToPython>::convert(const void*) [with T = AtomicTest; ToPython = boost::python::objects::class_cref_wrapper<AtomicTest, boost::python::objects::make_instance<AtomicTest, boost::python::objects::value_holder<AtomicTest> > >; PyObject = _object]’
/usr/include/boost/python/to_python_converter.hpp:83:9:   [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/python/object/class_metadata.hpp:227:55:   required from ‘static void boost::python::objects::class_metadata<T, X1, X2, X3>::register_aux2(T2*, Callback) [with T2 = AtomicTest; Callback = boost::integral_constant<bool, false>; T = AtomicTest; X1 = boost::python::detail::not_specified; X2 = boost::python::detail::not_specified; X3 = boost::python::detail::not_specified]’
/usr/include/boost/python/object/class_metadata.hpp:218:38:   required from ‘static void boost::python::objects::class_metadata<T, X1, X2, X3>::register_aux(void*) [with T = AtomicTest; X1 = boost::python::detail::not_specified; X2 = boost::python::detail::not_specified; X3 = boost::python::detail::not_specified]’
/usr/include/boost/python/object/class_metadata.hpp:204:37:   required from ‘static void boost::python::objects::class_metadata<T, X1, X2, X3>::register_() [with T = AtomicTest; X1 = boost::python::detail::not_specified; X2 = boost::python::detail::not_specified; X3 = boost::python::detail::not_specified]’
/usr/include/boost/python/class.hpp:450:28:   required from ‘void boost::python::class_<T, X1, X2, X3>::initialize(const DefVisitor&) [with DefVisitor = boost::python::init<>; W = AtomicTest; X1 = boost::python::detail::not_specified; X2 = boost::python::detail::not_specified; X3 = boost::python::detail::not_specified]’
/usr/include/boost/python/class.hpp:583:5:   required from ‘boost::python::class_<T, X1, X2, X3>::class_(const char*, const char*) [with W = AtomicTest; X1 = boost::python::detail::not_specified; X2 = boost::python::detail::not_specified; X3 = boost::python::detail::not_specified]’
/home/tb/CLionProjects/atomicTest/AtomicTest.cpp:17:48:   required from here
/usr/include/boost/python/object/value_holder.hpp:133:13: error: use of deleted function ‘AtomicTest::AtomicTest(const AtomicTest&)’
             BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_UNFORWARD_LOCAL, nil)
             ^
In file included from /home/tb/CLionProjects/atomicTest/AtomicTest.cpp:5:0:
/home/tb/CLionProjects/atomicTest/AtomicTest.h:10:7: note: ‘AtomicTest::AtomicTest(const AtomicTest&)’ is implicitly deleted because the default definition would be ill-formed:
 class AtomicTest
       ^~~~~~~~~~
/home/tb/CLionProjects/atomicTest/AtomicTest.h:10:7: error: use of deleted function ‘std::atomic<unsigned int>::atomic(const std::atomic<unsigned int>&)’
In file included from /home/tb/CLionProjects/atomicTest/AtomicTest.h:8:0,
                 from /home/tb/CLionProjects/atomicTest/AtomicTest.cpp:5:
/usr/include/c++/7/atomic:691:7: note: declared here
       atomic(const atomic&) = delete;

【问题讨论】:

【参考方案1】:

当 Python 接口初始化其可从 Python 获得的对象时,它会复制您的对象。由于您的类具有默认的复制构造函数,因此它也会复制每个字段,并且原子对象没有复制构造函数(或赋值),对于good reason。

去这里的方法是告诉boost不要使用副本,使用noncopyable(必须在那里搜索)。这个问题以boost::python: compilation fails because copy constructor is private 为例,在您的情况下,您需要更改class_ 定义:

class_<AtomicTest, boost::noncopyable>("AtomicTest")

您也可以选择将您的原子序数保存在(共享)指针中,以便所有副本看起来都相同。这在一些将多个对象同步到同一个计数器的设计中通常是有意义的:

class AtomicTest

public:
    AtomicTest() : v1(std::make_shared<std::atomic_uint32_t>(0)) 
    void init();
    std::shared_ptr<std::atomic_uint32_t> v1;
;

如果您的用例是一个单独计数的对象,那么noncopyable 可能更合适。

【讨论】:

boost::noncopyable 解决了很多编译器错误。谢谢!【参考方案2】:

这是有道理的,您只需要对代码进行一些调整即可编译它

std::atomic_uint32_t == std::atomic<std::uint32_t>

如果你看标题https://en.cppreference.com/w/cpp/header/atomic

下 整数类型的 std::atomic 特化

    atomic(const atomic&) = delete;
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;

另请参阅此参考 https://en.cppreference.com/w/cpp/atomic/atomic/atomic

    原子变量不是 CopyConstructible。

警告:下面不安全

但是买家要注意这个操作不会是原子的并且可能会变得很奇怪

阅读此答案以了解有关原子不可复制的更多原因 Why are std::atomic objects not copyable?

所以理论上你可以做的是显式定义一个复制构造函数并这样做

AtomicTest::AtomicTest(const AtomicTest& origin)
: v1(origin.v1.load())

//... more explicit copying

【讨论】:

我不建议为原子内容提供复制构造函数。你提到了为什么不这样做的原因。 是的,我同意。不知道boost::noncopyable。很好的发现。这是更好的答案

以上是关于std::atomic 作为类成员:使用 boost/python.hpp 时使用已删除的函数错误的主要内容,如果未能解决你的问题,请参考以下文章

初始化 std::atomic_bool?

将 std::atomic_flag 包装在 getter/setter 中会使其“原子性”无效吗?

C++ 原子操作 std::atomic<int>

std::atomic_flag 初始化结果

C++并发与多线程 11_std::atomic叙谈std::launch(std::async) 深入

用 std::atomic 实现的 shared_lock