From 9a111213f81ad2c4c008b5a17b82aa391cd6f267 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 24 Jul 2015 01:59:39 -0700 Subject: [PATCH 1/8] rework of aligned storage based on aligned_storage -introduce alignment_of, for types needing overriding such as Eigen's fixed types -use aligned_storage for referent and instance storage --- doc/v2/faq.html | 2 +- doc/v2/pytype_function.html | 2 +- .../python/converter/arg_from_python.hpp | 8 ++-- include/boost/python/converter/implicit.hpp | 2 +- .../converter/obj_mgr_arg_from_python.hpp | 10 ++--- .../converter/rvalue_from_python_data.hpp | 9 +++-- .../converter/shared_ptr_from_python.hpp | 2 +- include/boost/python/detail/alignment_of.hpp | 21 +++++++++++ .../boost/python/detail/referent_storage.hpp | 37 +++---------------- include/boost/python/enum.hpp | 2 +- include/boost/python/extract.hpp | 4 +- include/boost/python/object/instance.hpp | 20 +++++----- include/boost/python/object/make_instance.hpp | 5 ++- src/converter/builtin_converters.cpp | 2 +- src/converter/from_python.cpp | 2 +- test/a_map_indexing_suite.cpp | 2 +- test/pytype_function.cpp | 2 +- 17 files changed, 67 insertions(+), 65 deletions(-) create mode 100644 include/boost/python/detail/alignment_of.hpp diff --git a/doc/v2/faq.html b/doc/v2/faq.html index 75283d77ce..6019aada3f 100644 --- a/doc/v2/faq.html +++ b/doc/v2/faq.html @@ -769,7 +769,7 @@

How can I automatically if (value == 0) boost::python::throw_error_already_set(); void* storage = ( (boost::python::converter::rvalue_from_python_storage<custom_string>*) - data)->storage.bytes; + data)->storage.address(); new (storage) custom_string(value); data->convertible = storage; } diff --git a/doc/v2/pytype_function.html b/doc/v2/pytype_function.html index fcc2a7f928..0969519a93 100644 --- a/doc/v2/pytype_function.html +++ b/doc/v2/pytype_function.html @@ -329,7 +329,7 @@

C++ module definition

converter::rvalue_from_python_stage1_data* data) { void* storage = ( - (converter::rvalue_from_python_storage< B >*)data)-> storage.bytes; + (converter::rvalue_from_python_storage< B >*)data)-> storage.address(); extract<const A&> ex(obj_ptr); new (storage) B(ex()); diff --git a/include/boost/python/converter/arg_from_python.hpp b/include/boost/python/converter/arg_from_python.hpp index 61bbaad570..bfc7c25a5a 100644 --- a/include/boost/python/converter/arg_from_python.hpp +++ b/include/boost/python/converter/arg_from_python.hpp @@ -242,7 +242,7 @@ inline pointer_cref_arg_from_python::pointer_cref_arg_from_python(PyObject* p // indicates success. If find returns nonzero, it's a pointer to // a U object. python::detail::write_void_ptr_reference( - m_result.bytes + m_result.address() , p == Py_None ? p : converter::get_lvalue_from_python(p, registered_pointee::converters) , (T(*)())0); } @@ -250,15 +250,15 @@ inline pointer_cref_arg_from_python::pointer_cref_arg_from_python(PyObject* p template inline bool pointer_cref_arg_from_python::convertible() const { - return python::detail::void_ptr_to_reference(m_result.bytes, (T(*)())0) != 0; + return python::detail::void_ptr_to_reference(m_result.address(), (T(*)())0) != 0; } template inline T pointer_cref_arg_from_python::operator()() const { - return (*(void**)m_result.bytes == Py_None) // None ==> 0 + return (*(void**)m_result.address() == Py_None) // None ==> 0 ? detail::null_ptr_reference((T(*)())0) // Otherwise, return a U*const& to the m_result storage. - : python::detail::void_ptr_to_reference(m_result.bytes, (T(*)())0); + : python::detail::void_ptr_to_reference(m_result.address(), (T(*)())0); } // pointer_arg_from_python diff --git a/include/boost/python/converter/implicit.hpp b/include/boost/python/converter/implicit.hpp index 8bbbfd5ac1..e05674bfee 100644 --- a/include/boost/python/converter/implicit.hpp +++ b/include/boost/python/converter/implicit.hpp @@ -28,7 +28,7 @@ struct implicit static void construct(PyObject* obj, rvalue_from_python_stage1_data* data) { - void* storage = ((rvalue_from_python_storage*)data)->storage.bytes; + void* storage = ((rvalue_from_python_storage*)data)->storage.address(); arg_from_python get_source(obj); bool convertible = get_source.convertible(); diff --git a/include/boost/python/converter/obj_mgr_arg_from_python.hpp b/include/boost/python/converter/obj_mgr_arg_from_python.hpp index cd4e1e0ea8..8c444108f8 100644 --- a/include/boost/python/converter/obj_mgr_arg_from_python.hpp +++ b/include/boost/python/converter/obj_mgr_arg_from_python.hpp @@ -81,16 +81,16 @@ inline object_manager_ref_arg_from_python::object_manager_ref_arg_from_pyth # if defined(__EDG_VERSION__) && __EDG_VERSION__ <= 243 // needed for warning suppression python::detail::borrowed_reference x_ = python::detail::borrowed_reference(x); - python::detail::construct_referent(&m_result.bytes, x_); + python::detail::construct_referent(m_result.address(), x_); # else - python::detail::construct_referent(&m_result.bytes, (python::detail::borrowed_reference)x); + python::detail::construct_referent(m_result.address(), (python::detail::borrowed_reference)x); # endif } template inline object_manager_ref_arg_from_python::~object_manager_ref_arg_from_python() { - python::detail::destroy_referent(this->m_result.bytes); + python::detail::destroy_referent(this->m_result.address()); } namespace detail @@ -106,14 +106,14 @@ template inline bool object_manager_ref_arg_from_python::convertible() const { return detail::object_manager_ref_check( - python::detail::void_ptr_to_reference(this->m_result.bytes, (Ref(*)())0)); + python::detail::void_ptr_to_reference(this->m_result.address(), (Ref(*)())0)); } template inline Ref object_manager_ref_arg_from_python::operator()() const { return python::detail::void_ptr_to_reference( - this->m_result.bytes, (Ref(*)())0); + this->m_result.address(), (Ref(*)())0); } }}} // namespace boost::python::converter diff --git a/include/boost/python/converter/rvalue_from_python_data.hpp b/include/boost/python/converter/rvalue_from_python_data.hpp index 471a5255b6..a4298ffcba 100644 --- a/include/boost/python/converter/rvalue_from_python_data.hpp +++ b/include/boost/python/converter/rvalue_from_python_data.hpp @@ -83,7 +83,7 @@ struct rvalue_from_python_storage }; // Augments rvalue_from_python_storage with a destructor. If -// stage1.convertible == storage.bytes, it indicates that an object of +// stage1.convertible == storage.address(), it indicates that an object of // remove_reference::type has been constructed in storage and // should will be destroyed in ~rvalue_from_python_data(). It is // crucial that successful rvalue conversions establish this equality @@ -131,8 +131,11 @@ inline rvalue_from_python_data::rvalue_from_python_data(void* convertible) template inline rvalue_from_python_data::~rvalue_from_python_data() { - if (this->stage1.convertible == this->storage.bytes) - python::detail::destroy_referent(this->storage.bytes); + if (this->stage1.convertible == this->storage.address()){ + size_t allocated = sizeof(this->storage); + void* aligned_storage = ::boost::align(::boost::python::alignment_of::value, 0, this->storage.address(), allocated); + python::detail::destroy_referent(aligned_storage); + } } }}} // namespace boost::python::converter diff --git a/include/boost/python/converter/shared_ptr_from_python.hpp b/include/boost/python/converter/shared_ptr_from_python.hpp index c09107765c..cbf8ca7dde 100644 --- a/include/boost/python/converter/shared_ptr_from_python.hpp +++ b/include/boost/python/converter/shared_ptr_from_python.hpp @@ -40,7 +40,7 @@ struct shared_ptr_from_python static void construct(PyObject* source, rvalue_from_python_stage1_data* data) { - void* const storage = ((converter::rvalue_from_python_storage >*)data)->storage.bytes; + void* const storage = ((converter::rvalue_from_python_storage >*)data)->storage.address(); // Deal with the "None" case. if (data->convertible == source) new (storage) shared_ptr(); diff --git a/include/boost/python/detail/alignment_of.hpp b/include/boost/python/detail/alignment_of.hpp new file mode 100644 index 0000000000..bc6f8b2176 --- /dev/null +++ b/include/boost/python/detail/alignment_of.hpp @@ -0,0 +1,21 @@ +// Copyright Jason Newton 2015 +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef ALIGNMENT_OF_DWA2003224_HPP +# define ALIGNMENT_OF_DWA2003224_HPP + +# include +# include + +namespace boost { namespace python { namespace detail { + + //allow users to override the alignment through partial template specialization/SFINAE + template + struct alignment_of : public ::boost::alignment::alignment_of{ + }; + +}}} // namespace boost::python::detail + +#endif // ALIGNMENT_OF_DWA2003224_HPP + diff --git a/include/boost/python/detail/referent_storage.hpp b/include/boost/python/detail/referent_storage.hpp index 2cddf696d5..53330c7c14 100644 --- a/include/boost/python/detail/referent_storage.hpp +++ b/include/boost/python/detail/referent_storage.hpp @@ -6,38 +6,12 @@ # define REFERENT_STORAGE_DWA200278_HPP # include # include +# include +# include +# include namespace boost { namespace python { namespace detail { -struct alignment_dummy; -typedef void (*function_ptr)(); -typedef int (alignment_dummy::*member_ptr); -typedef int (alignment_dummy::*member_function_ptr)(); - -# define BOOST_PYTHON_ALIGNER(T, n) \ - typename mpl::if_c< \ - sizeof(T) <= size, T, char>::type t##n - -// Storage for size bytes, aligned to all fundamental types no larger than size -template -union aligned_storage -{ - BOOST_PYTHON_ALIGNER(char, 0); - BOOST_PYTHON_ALIGNER(short, 1); - BOOST_PYTHON_ALIGNER(int, 2); - BOOST_PYTHON_ALIGNER(long, 3); - BOOST_PYTHON_ALIGNER(float, 4); - BOOST_PYTHON_ALIGNER(double, 5); - BOOST_PYTHON_ALIGNER(long double, 6); - BOOST_PYTHON_ALIGNER(void*, 7); - BOOST_PYTHON_ALIGNER(function_ptr, 8); - BOOST_PYTHON_ALIGNER(member_ptr, 9); - BOOST_PYTHON_ALIGNER(member_function_ptr, 10); - char bytes[size]; -}; - -# undef BOOST_PYTHON_ALIGNER - // Compute the size of T's referent. We wouldn't need this at all, // but sizeof() is broken in CodeWarriors <= 8.0 template struct referent_size; @@ -56,9 +30,8 @@ union aligned_storage template struct referent_storage { - typedef aligned_storage< - ::boost::python::detail::referent_size::value - > type; + typedef typename ::boost::aligned_storage< ::boost::python::detail::referent_size::value, alignment_of::value>::type type; + type storage; }; }}} // namespace boost::python::detail diff --git a/include/boost/python/enum.hpp b/include/boost/python/enum.hpp index 9631a0edc8..ee5241b9ce 100644 --- a/include/boost/python/enum.hpp +++ b/include/boost/python/enum.hpp @@ -84,7 +84,7 @@ void enum_::construct(PyObject* obj, converter::rvalue_from_python_stage1_dat #else T x = static_cast(PyInt_AS_LONG(obj)); #endif - void* const storage = ((converter::rvalue_from_python_storage*)data)->storage.bytes; + void* const storage = ((converter::rvalue_from_python_storage*)data)->storage.address(); new (storage) T(x); data->convertible = storage; } diff --git a/include/boost/python/extract.hpp b/include/boost/python/extract.hpp index bfdeb83ce7..203a4dccd5 100644 --- a/include/boost/python/extract.hpp +++ b/include/boost/python/extract.hpp @@ -179,8 +179,8 @@ namespace converter { return *(T*)( // Only do the stage2 conversion once - m_data.stage1.convertible == m_data.storage.bytes - ? m_data.storage.bytes + m_data.stage1.convertible == m_data.storage.address() + ? m_data.storage.address() : (rvalue_from_python_stage2)(m_source, m_data.stage1, registered::converters) ); } diff --git a/include/boost/python/object/instance.hpp b/include/boost/python/object/instance.hpp index 177576ef82..57316ae624 100644 --- a/include/boost/python/object/instance.hpp +++ b/include/boost/python/object/instance.hpp @@ -7,11 +7,20 @@ # include # include +# include +# include +# include # include namespace boost { namespace python { struct BOOST_PYTHON_DECL_FORWARD instance_holder; + + //allow users to override the alignment through partial template specialization/SFINAE + template + struct alignment_of : public ::boost::alignment::alignment_of{ + }; + }} // namespace boost::python namespace boost { namespace python { namespace objects { @@ -25,15 +34,8 @@ struct instance PyObject* weakrefs; instance_holder* objects; - typedef typename type_with_alignment< - ::boost::alignment_of::value - >::type align_t; - - union - { - align_t align; - char bytes[sizeof(Data)]; - } storage; + typedef typename ::boost::aligned_storage::value>::type storage_t; + storage_t storage; }; template diff --git a/include/boost/python/object/make_instance.hpp b/include/boost/python/object/make_instance.hpp index 5f2630adc7..23084c00ba 100644 --- a/include/boost/python/object/make_instance.hpp +++ b/include/boost/python/object/make_instance.hpp @@ -6,6 +6,7 @@ # define MAKE_INSTANCE_DWA200296_HPP # include +# include # include # include # include @@ -13,6 +14,8 @@ # include # include # include +# include +# include namespace boost { namespace python { namespace objects { @@ -42,7 +45,7 @@ struct make_instance_impl // construct the new C++ object and install the pointer // in the Python object. - Derived::construct(&instance->storage, (PyObject*)instance, x)->install(raw_result); + Derived::construct(instance->storage.address(), (PyObject*)instance, x)->install(raw_result); // Note the position of the internally-stored Holder, // for the sake of destruction diff --git a/src/converter/builtin_converters.cpp b/src/converter/builtin_converters.cpp index 9900602b77..32d9de346c 100644 --- a/src/converter/builtin_converters.cpp +++ b/src/converter/builtin_converters.cpp @@ -83,7 +83,7 @@ namespace handle<> intermediate(creator(obj)); // Get the location in which to construct - void* storage = ((rvalue_from_python_storage*)data)->storage.bytes; + void* storage = ((rvalue_from_python_storage*)data)->storage.address(); # ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4244) diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index 9678be1cb6..b1900b2d68 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -33,7 +33,7 @@ namespace boost { namespace python { namespace converter { // // 3. where y is of type rvalue_from_python_data, // x.construct(source, y) constructs an object of type T -// in y.storage.bytes and then sets y.convertible == y.storage.bytes, +// in y.storage.address() and then sets y.convertible == y.storage.address(), // or else throws an exception and has no effect. BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( PyObject* source diff --git a/test/a_map_indexing_suite.cpp b/test/a_map_indexing_suite.cpp index 07a0a6b977..5b018c0c62 100644 --- a/test/a_map_indexing_suite.cpp +++ b/test/a_map_indexing_suite.cpp @@ -62,7 +62,7 @@ struct AFromPython { void* storage = ( (boost::python::converter::rvalue_from_python_storage< A >*) - data)-> storage.bytes; + data)-> storage.address(); #if PY_VERSION_HEX >= 0x03000000 new (storage) A((int)PyLong_AsLong(obj_ptr)); diff --git a/test/pytype_function.cpp b/test/pytype_function.cpp index 46cce19e65..b72d730efb 100644 --- a/test/pytype_function.cpp +++ b/test/pytype_function.cpp @@ -59,7 +59,7 @@ struct BFromPython boost::python::converter::rvalue_from_python_stage1_data* data) { void* storage = ( - (boost::python::converter::rvalue_from_python_storage< B >*)data)-> storage.bytes; + (boost::python::converter::rvalue_from_python_storage< B >*)data)-> storage.address(); extract ex(obj_ptr); new (storage) B(ex()); From 316df0762879695d2f24a7d0d822104f39df1e68 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 24 Jul 2015 02:00:35 -0700 Subject: [PATCH 2/8] respect alignment, allocate only on aligned addresses --- .../python/converter/rvalue_from_python_data.hpp | 3 ++- include/boost/python/object/instance.hpp | 4 ++-- include/boost/python/object/make_instance.hpp | 12 +++++++++--- include/boost/python/object/value_holder.hpp | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/include/boost/python/converter/rvalue_from_python_data.hpp b/include/boost/python/converter/rvalue_from_python_data.hpp index a4298ffcba..380d248459 100644 --- a/include/boost/python/converter/rvalue_from_python_data.hpp +++ b/include/boost/python/converter/rvalue_from_python_data.hpp @@ -133,7 +133,8 @@ inline rvalue_from_python_data::~rvalue_from_python_data() { if (this->stage1.convertible == this->storage.address()){ size_t allocated = sizeof(this->storage); - void* aligned_storage = ::boost::align(::boost::python::alignment_of::value, 0, this->storage.address(), allocated); + void * ptr = this->storage.address(); + void* aligned_storage = ::boost::alignment::align(detail::alignment_of::value, 0, ptr, allocated); python::detail::destroy_referent(aligned_storage); } } diff --git a/include/boost/python/object/instance.hpp b/include/boost/python/object/instance.hpp index 57316ae624..b3bcd756ac 100644 --- a/include/boost/python/object/instance.hpp +++ b/include/boost/python/object/instance.hpp @@ -35,7 +35,7 @@ struct instance instance_holder* objects; typedef typename ::boost::aligned_storage::value>::type storage_t; - storage_t storage; + BOOST_ALIGNMENT(::boost::python::detail::alignment_of::value+0) storage_t storage; }; template @@ -45,7 +45,7 @@ struct additional_instance_size typedef instance instance_char; BOOST_STATIC_CONSTANT( std::size_t, value = sizeof(instance_data) - - BOOST_PYTHON_OFFSETOF(instance_char,storage)); + - BOOST_PYTHON_OFFSETOF(instance_char,storage) + detail::alignment_of::value); }; }}} // namespace boost::python::object diff --git a/include/boost/python/object/make_instance.hpp b/include/boost/python/object/make_instance.hpp index 23084c00ba..0af4c77fa0 100644 --- a/include/boost/python/object/make_instance.hpp +++ b/include/boost/python/object/make_instance.hpp @@ -45,11 +45,13 @@ struct make_instance_impl // construct the new C++ object and install the pointer // in the Python object. - Derived::construct(instance->storage.address(), (PyObject*)instance, x)->install(raw_result); + Holder *holder =Derived::construct(instance->storage.address(), (PyObject*)instance, x); + holder->install(raw_result); // Note the position of the internally-stored Holder, // for the sake of destruction - Py_SIZE(instance) = offsetof(instance_t, storage); + const size_t offset = reinterpret_cast(holder) - reinterpret_cast(instance->storage.address()) + offsetof(instance_t, storage); + Py_SIZE(instance) = offset; // Release ownership of the python object protect.cancel(); @@ -63,6 +65,8 @@ template struct make_instance : make_instance_impl > { + typedef typename make_instance_impl >::instance_t instance_t; + template static inline PyTypeObject* get_class_object(U&) { @@ -71,7 +75,9 @@ struct make_instance static inline Holder* construct(void* storage, PyObject* instance, reference_wrapper x) { - return new (storage) Holder(instance, x); + size_t allocated = objects::additional_instance_size::value; + void* aligned_storage = ::boost::alignment::align(detail::alignment_of::value, sizeof(Holder), storage, allocated); + return new (aligned_storage) Holder(instance, x); } }; diff --git a/include/boost/python/object/value_holder.hpp b/include/boost/python/object/value_holder.hpp index a4e91786d1..1ce2cc3a2b 100644 --- a/include/boost/python/object/value_holder.hpp +++ b/include/boost/python/object/value_holder.hpp @@ -59,7 +59,7 @@ struct value_holder : instance_holder return 0; } private: // data members - Value m_held; + BOOST_ALIGNMENT(::boost::python::detail::alignment_of::value + 0) Value m_held; }; template From 24281aec47d0ee5aa4dbb82cc456dee68f21992d Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 26 Jul 2015 17:40:41 -0700 Subject: [PATCH 3/8] add an aligned-class test case --- test/Jamfile.v2 | 1 + test/aligned_class.cpp | 37 +++++++++++++++++++++++++++++++++++ test/aligned_class.py | 44 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 test/aligned_class.cpp create mode 100755 test/aligned_class.py diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 5524188003..51262a923b 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -122,6 +122,7 @@ bpl-test crossmod_exception [ bpl-test object ] [ bpl-test class ] +[ bpl-test aligned_class ] [ bpl-test list ] [ bpl-test long ] [ bpl-test dict ] diff --git a/test/aligned_class.cpp b/test/aligned_class.cpp new file mode 100644 index 0000000000..3de6982b44 --- /dev/null +++ b/test/aligned_class.cpp @@ -0,0 +1,37 @@ +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include +#include +#include +#include +#include + +using namespace boost::python; + +struct BOOST_ALIGNMENT(32) X +{ + int x; + BOOST_ALIGNMENT(32) float f; + X(int n, float _f) : x(n), f(_f){ + BOOST_ASSERT((reinterpret_cast(&f) % 32) == 0); + } +}; + +int x_function(X& x) +{ return x.x; +} + +float f_function(X& x) +{ return x.f; +} + + +BOOST_PYTHON_MODULE(aligned_class_ext) +{ + class_("X", init()); + def("x_function", x_function); + def("f_function", f_function); +} + +#include "module_tail.cpp" diff --git a/test/aligned_class.py b/test/aligned_class.py new file mode 100755 index 0000000000..25bf9c9c7f --- /dev/null +++ b/test/aligned_class.py @@ -0,0 +1,44 @@ +# Distributed under the Boost +# Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +''' +>>> from aligned_class_ext import * + +Ensure sanity: + + >>> x = X(42, 16) + >>> x_function(x) + 42 + >>> f_function(x) + 16.0 + +Demonstrate extraction in the presence of metaclass changes: + + >>> class MetaX(X.__class__): + ... def __new__(cls, *args): + ... return super(MetaX, cls).__new__(cls, *args) + >>> class XPlusMetatype(X): + ... __metaclass__ = MetaX + >>> x = XPlusMetatype(42, 16) + >>> x_function(x) + 42 + >>> f_function(x) + 16.0 + + +''' + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + status = run()[0] + if (status == 0): print "Done." + sys.exit(status) From 3de8f71b64f1814b3eb670d34a6116d5edb2076e Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 26 Jul 2015 19:19:30 -0700 Subject: [PATCH 4/8] respecting alignment part 2: support dynamic allocations and allocations made through make_holder --- include/boost/python/instance_holder.hpp | 2 +- include/boost/python/object/make_holder.hpp | 7 ++--- src/object/class.cpp | 35 ++++++++++++++++----- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/include/boost/python/instance_holder.hpp b/include/boost/python/instance_holder.hpp index 933f50d1a1..f4ed1e6608 100644 --- a/include/boost/python/instance_holder.hpp +++ b/include/boost/python/instance_holder.hpp @@ -38,7 +38,7 @@ struct BOOST_PYTHON_DECL instance_holder : private noncopyable // Allocate storage for an object of the given size at the given // offset in the Python instance<> object if bytes are available // there. Otherwise allocate size bytes of heap memory. - static void* allocate(PyObject*, std::size_t offset, std::size_t size); + static void* allocate(PyObject*, std::size_t offset, std::size_t size, std::size_t alignment = 1); // Deallocate storage from the heap if it was not carved out of // the given Python object by allocate(), above. diff --git a/include/boost/python/object/make_holder.hpp b/include/boost/python/object/make_holder.hpp index 0d54dd9f66..e687692a6d 100644 --- a/include/boost/python/object/make_holder.hpp +++ b/include/boost/python/object/make_holder.hpp @@ -89,14 +89,13 @@ struct make_holder BOOST_PP_ENUM_TRAILING_BINARY_PARAMS_Z(1, N, t, a)) { typedef instance instance_t; - - void* memory = Holder::allocate(p, offsetof(instance_t, storage), sizeof(Holder)); + void* aligned_memory = Holder::allocate(p, offsetof(instance_t, storage), sizeof(Holder), detail::alignment_of::value); try { - (new (memory) Holder( + (new (aligned_memory) Holder( p BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_DO_FORWARD_ARG, nil)))->install(p); } catch(...) { - Holder::deallocate(p, memory); + Holder::deallocate(p, aligned_memory); throw; } } diff --git a/src/object/class.cpp b/src/object/class.cpp index aeef688e28..922a02aa3a 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -726,28 +726,45 @@ namespace objects } // namespace objects -void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std::size_t holder_size) +typedef unsigned int alignment_marker_t; + +void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std::size_t holder_size, std::size_t alignment) { assert(PyType_IsSubtype(Py_TYPE(Py_TYPE(self_)), &class_metatype_object)); objects::instance<>* self = (objects::instance<>*)self_; - int total_size_needed = holder_offset + holder_size; + int total_size_needed = holder_offset + holder_size + alignment - 1; if (-Py_SIZE(self) >= total_size_needed) { // holder_offset should at least point into the variable-sized part assert(holder_offset >= offsetof(objects::instance<>,storage)); + size_t allocated = holder_size + alignment; + void* storage = (char*)self + holder_offset; + void* aligned_storage = ::boost::alignment::align(alignment, holder_size, storage, allocated); + // Record the fact that the storage is occupied, noting where it starts - Py_SIZE(self) = holder_offset; - return (char*)self + holder_offset; + const size_t offset = reinterpret_cast(aligned_storage) - reinterpret_cast(storage) + holder_offset; + Py_SIZE(self) = offset; + return (char*)self + offset; } else { - void* const result = PyMem_Malloc(holder_size); - if (result == 0) + const size_t base_allocation = holder_size + alignment - 1 + sizeof(alignment_marker_t); + void* const base_storage = PyMem_Malloc(base_allocation); + if (base_storage == 0) throw std::bad_alloc(); - return result; + + const size_t x = reinterpret_cast(base_storage); + //this has problems for x -> max(void *) + //const size_t padding = alignment - ((x + sizeof(alignment_marker_t)) % alignment); + //only works for alignments with alignments of powers of 2, but no edge conditions + const size_t padding = alignment - ((x + sizeof(alignment_marker_t)) & (alignment - 1)); + void* const aligned_storage = (char *)base_storage + padding; + alignment_marker_t* const marker_storage = reinterpret_cast((char *)aligned_storage - sizeof(alignment_marker_t)); + *marker_storage = static_cast(padding); + return aligned_storage; } } @@ -757,7 +774,9 @@ void instance_holder::deallocate(PyObject* self_, void* storage) throw() objects::instance<>* self = (objects::instance<>*)self_; if (storage != (char*)self + Py_SIZE(self)) { - PyMem_Free(storage); + alignment_marker_t* marker_storage = reinterpret_cast((char *)storage - sizeof(alignment_marker_t)); + void *malloced_storage = (char *) storage - (*marker_storage); + PyMem_Free(malloced_storage); } } From f50302d9229abe5a218827efa67f721e3c4af998 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 26 Jul 2015 20:00:37 -0700 Subject: [PATCH 5/8] use boost::make_shared to respect allocation --- test/injected.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/injected.cpp b/test/injected.cpp index 73e1e14baa..7fe2795ed5 100755 --- a/test/injected.cpp +++ b/test/injected.cpp @@ -8,6 +8,7 @@ #include "test_class.hpp" #include #include +#include #include #include @@ -21,7 +22,7 @@ std::auto_ptr sum(int a, int b) { return std::auto_ptr(new X(a+b)); } boost::shared_ptr product(int a, int b, int c) { - return boost::shared_ptr(new X(a*b*c)); + return boost::make_shared(a*b*c); } BOOST_PYTHON_MODULE(injected_ext) From 5d37769853b558a0ec710983b3c4a94494257a97 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 26 Jul 2015 20:48:54 -0700 Subject: [PATCH 6/8] respecting alignment part 3: allocations made through instance_holder::allocate --- src/object/class.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/object/class.cpp b/src/object/class.cpp index 922a02aa3a..133b0a2f84 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -751,17 +751,19 @@ void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std: } else { - const size_t base_allocation = holder_size + alignment - 1 + sizeof(alignment_marker_t); + const size_t base_allocation = sizeof(alignment_marker_t) + holder_size + alignment - 1; void* const base_storage = PyMem_Malloc(base_allocation); if (base_storage == 0) throw std::bad_alloc(); - const size_t x = reinterpret_cast(base_storage); + const size_t x = reinterpret_cast(base_storage) + sizeof(alignment_marker_t); //this has problems for x -> max(void *) //const size_t padding = alignment - ((x + sizeof(alignment_marker_t)) % alignment); //only works for alignments with alignments of powers of 2, but no edge conditions - const size_t padding = alignment - ((x + sizeof(alignment_marker_t)) & (alignment - 1)); - void* const aligned_storage = (char *)base_storage + padding; + const size_t padding = alignment == 1 ? 0 : ( alignment - (x & (alignment - 1)) ); + const size_t aligned_offset = sizeof(alignment_marker_t) + padding; + void* const aligned_storage = (char *)base_storage + aligned_offset; + BOOST_ASSERT((char *) aligned_storage + holder_size <= (char *)base_storage + base_allocation); alignment_marker_t* const marker_storage = reinterpret_cast((char *)aligned_storage - sizeof(alignment_marker_t)); *marker_storage = static_cast(padding); return aligned_storage; @@ -775,7 +777,7 @@ void instance_holder::deallocate(PyObject* self_, void* storage) throw() if (storage != (char*)self + Py_SIZE(self)) { alignment_marker_t* marker_storage = reinterpret_cast((char *)storage - sizeof(alignment_marker_t)); - void *malloced_storage = (char *) storage - (*marker_storage); + void *malloced_storage = (char *) storage - sizeof(alignment_marker_t) - (*marker_storage); PyMem_Free(malloced_storage); } } From 35747f838865cc780eab905709f9de5e3fdbaec8 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 2 Aug 2015 01:51:17 -0700 Subject: [PATCH 7/8] backwards compatibility with previous storage fieldname bytes --- include/boost/python/detail/referent_storage.hpp | 13 ++++++++++++- include/boost/python/object/instance.hpp | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/boost/python/detail/referent_storage.hpp b/include/boost/python/detail/referent_storage.hpp index 53330c7c14..88397dca01 100644 --- a/include/boost/python/detail/referent_storage.hpp +++ b/include/boost/python/detail/referent_storage.hpp @@ -24,13 +24,24 @@ namespace boost { namespace python { namespace detail { std::size_t, value = sizeof(T)); }; + //compatability union + template + union aligned_storage_t{ + typedef typename ::boost::aligned_storage< ::boost::python::detail::referent_size::value, alignment_of::value>::type type; + type storage; + char bytes[sizeof(type)]; + + void * address() const{ + return storage.address(); + } + }; // A metafunction returning a POD type which can store U, where T == // U&. If T is not a reference type, returns a POD which can store T. template struct referent_storage { - typedef typename ::boost::aligned_storage< ::boost::python::detail::referent_size::value, alignment_of::value>::type type; + typedef aligned_storage_t type; type storage; }; diff --git a/include/boost/python/object/instance.hpp b/include/boost/python/object/instance.hpp index b3bcd756ac..a7525046f0 100644 --- a/include/boost/python/object/instance.hpp +++ b/include/boost/python/object/instance.hpp @@ -34,8 +34,19 @@ struct instance PyObject* weakrefs; instance_holder* objects; - typedef typename ::boost::aligned_storage::value>::type storage_t; - BOOST_ALIGNMENT(::boost::python::detail::alignment_of::value+0) storage_t storage; + + //compatability union + union aligned_storage_t{ + typedef typename ::boost::aligned_storage< sizeof(Data), detail::alignment_of::value>::type type; + type storage; + char bytes[sizeof(type)]; + + void * address() const{ + return storage.address(); + } + }; + + BOOST_ALIGNMENT(::boost::python::detail::alignment_of::value+0) aligned_storage_t storage; }; template From 701e88843550fd1d5917745d0948c04d72c97daa Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 2 Aug 2015 19:57:30 -0700 Subject: [PATCH 8/8] use uintptr, not size_t --- include/boost/python/detail/config.hpp | 3 ++- src/object/class.cpp | 7 ++++--- test/aligned_class.cpp | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/boost/python/detail/config.hpp b/include/boost/python/detail/config.hpp index 1857d39a40..6a9fc76c98 100644 --- a/include/boost/python/detail/config.hpp +++ b/include/boost/python/detail/config.hpp @@ -14,6 +14,7 @@ # include # include +# include # ifdef BOOST_NO_OPERATORS_IN_NAMESPACE // A gcc bug forces some symbols into the global namespace @@ -106,7 +107,7 @@ #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) // Replace broken Tru64/cxx offsetof macro # define BOOST_PYTHON_OFFSETOF(s_name, s_member) \ - ((size_t)__INTADDR__(&(((s_name *)0)->s_member))) + ((boost::uintptr_t)__INTADDR__(&(((s_name *)0)->s_member))) #else # define BOOST_PYTHON_OFFSETOF offsetof #endif diff --git a/src/object/class.cpp b/src/object/class.cpp index 133b0a2f84..fcf4e81c7e 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -5,6 +5,7 @@ #include #include // #including this first is an intel6 workaround +#include #include #include @@ -745,7 +746,7 @@ void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std: void* aligned_storage = ::boost::alignment::align(alignment, holder_size, storage, allocated); // Record the fact that the storage is occupied, noting where it starts - const size_t offset = reinterpret_cast(aligned_storage) - reinterpret_cast(storage) + holder_offset; + const size_t offset = reinterpret_cast(aligned_storage) - reinterpret_cast(storage) + holder_offset; Py_SIZE(self) = offset; return (char*)self + offset; } @@ -756,11 +757,11 @@ void* instance_holder::allocate(PyObject* self_, std::size_t holder_offset, std: if (base_storage == 0) throw std::bad_alloc(); - const size_t x = reinterpret_cast(base_storage) + sizeof(alignment_marker_t); + const uintptr_t x = reinterpret_cast(base_storage) + sizeof(alignment_marker_t); //this has problems for x -> max(void *) //const size_t padding = alignment - ((x + sizeof(alignment_marker_t)) % alignment); //only works for alignments with alignments of powers of 2, but no edge conditions - const size_t padding = alignment == 1 ? 0 : ( alignment - (x & (alignment - 1)) ); + const uintptr_t padding = alignment == 1 ? 0 : ( alignment - (x & (alignment - 1)) ); const size_t aligned_offset = sizeof(alignment_marker_t) + padding; void* const aligned_storage = (char *)base_storage + aligned_offset; BOOST_ASSERT((char *) aligned_storage + holder_size <= (char *)base_storage + base_allocation); diff --git a/test/aligned_class.cpp b/test/aligned_class.cpp index 3de6982b44..0b499950ed 100644 --- a/test/aligned_class.cpp +++ b/test/aligned_class.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace boost::python; @@ -14,7 +15,7 @@ struct BOOST_ALIGNMENT(32) X int x; BOOST_ALIGNMENT(32) float f; X(int n, float _f) : x(n), f(_f){ - BOOST_ASSERT((reinterpret_cast(&f) % 32) == 0); + BOOST_ASSERT((reinterpret_cast(&f) % 32) == 0); } };