Search code examples
c++boost

std::atomic as class member: use of deleted function error when using boost/python.hpp


Other seemingly related questions appear to be resolved by doing direct initialization of the atomic:

std::atomic_uint32_t v1{0};

However, when I have a std::atomic variable as a class member I still get a "use of deleted function" error. I also only get the error when using Boost Python to wrap up my class so that it's callable from Python. I assume this is because Boost Python is applying additional compilation rules rather than some issue with 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 v1{0};
};


#endif //ATOMICTEST_ATOMICTEST_H

Build Log:

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;

Solution

  • When the Python interface initializes its objects that will be available from Python it copies your object. Since your class has the default copy constructor, it copies each field as well, and atomic objects do not have copy constructors (or assignments), and for good reason.

    The way to go here is to tell boost not to use copies, using noncopyable (have to search there). This question has this as an example boost::python: compilation fails because copy constructor is private, and in your case you need to change the class_ definition:

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

    Optionally you could also hold your atomic number in a (shared) pointer, so that all copies look at the same one. This often makes sense in some designs where multiple objects are synchronized to the same counter:

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

    If your use case though is a single object counting by itself though, then noncopyable is probably more suitable.