diff --git a/build/Jamfile b/build/Jamfile index 34f99dde73..4e16a2f146 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -48,6 +48,7 @@ lib boost_python tuple.cpp str.cpp slice.cpp + set.cpp converter/from_python.cpp converter/registry.cpp diff --git a/include/boost/python/set.hpp b/include/boost/python/set.hpp new file mode 100644 index 0000000000..350683e3ac --- /dev/null +++ b/include/boost/python/set.hpp @@ -0,0 +1,100 @@ +// Copyright Fady Essam 2019. 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 SET_BOOST_PYTHON_HH +#define SET_BOOST_PYTHON_HH + +#include +#include + + +namespace boost { + namespace python { + + namespace detail + { + struct BOOST_PYTHON_DECL set_base : object + { + void add(object_cref); // add object to set + + object pop(); // remove and return item at top + + void discard(object_cref x); // discard value from set + + long __len__(); // length of set + + void clear(); // empties set + + protected: + set_base(); + explicit set_base(object_cref sequence); // new set initialized from sequence's items + + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(set_base, object) + private: + static detail::new_non_null_reference call(object const&); + }; + + + } + + + + + + + class set : public detail::set_base + { + typedef detail::set_base base; + public: + set() {} + + template + explicit set(T const& sequence) + : base(object(sequence)) + { + } + + template + void add(T const& x) + { + base::add(object(x)); + } + + object pop() { return base::pop(); } + + template + void discard(T const& value) + { + base::discard(object(value)); + } + + void clear() { base::clear(); } + + long __len__() { return base::__len__(); } + + + + public: + BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(set, base) + }; + + + namespace converter + { + template <> + struct object_manager_traits + : pytype_object_manager_traits<&PySet_Type, set> + { + }; + } + + + } + +} + + + + + +#endif // !SET_BOOST_PYTHON_HH diff --git a/src/fabscript b/src/fabscript index 0ebeac6098..df9f6935ad 100644 --- a/src/fabscript +++ b/src/fabscript @@ -17,6 +17,7 @@ bpl = library('boost_python' + root.py_suffix, ['list.cpp', 'long.cpp', 'dict.cpp', + 'set.cpp', 'tuple.cpp', 'str.cpp', 'slice.cpp', diff --git a/src/set.cpp b/src/set.cpp new file mode 100644 index 0000000000..4abc463a71 --- /dev/null +++ b/src/set.cpp @@ -0,0 +1,83 @@ +// Copyright Fady Essam 2019. 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 + + +namespace boost { + namespace python { + namespace detail { + + + detail::new_non_null_reference set_base::call(object_cref arg_) + { + return (detail::new_non_null_reference) + (expect_non_null)( + PySet_New(arg_.ptr()) + ); + } + + set_base::set_base() + : object(detail::new_reference(PySet_New(NULL))) + {} + + set_base::set_base(object_cref sequence) + : object(set_base::call(sequence)) + {} + + void set_base::add(object_cref x) + { + if (PyAnySet_CheckExact(this->ptr())) + { + if (PySet_Add(this->ptr(), x.ptr()) == -1) + throw_error_already_set(); + } + else + { + this->attr("add")(x); + } + } + + + void set_base::discard(object_cref x) + { + if (PyAnySet_CheckExact(this->ptr())) + { + if (PySet_Discard(this->ptr(), x.ptr()) == -1) + throw_error_already_set(); + } + else + { + this->attr("discrad")(x); + } + } + + object set_base::pop() + { + return this->attr("pop")(); + } + + void set_base::clear() + { + this->attr("clear")(); + } + + long set_base::__len__() + { + return extract(object(PySet_Size(this->ptr())))(); + } + + + static struct register_set_pytype_ptr + { + register_set_pytype_ptr() + { + const_cast( + converter::registry::lookup(boost::python::type_id()) + ).m_class_object = &PySet_Type; + } + }register_set_pytype_ptr_; + + } + } +} diff --git a/test/Jamfile b/test/Jamfile index 9a5c756956..6d01673beb 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -135,6 +135,7 @@ bpl-test crossmod_exception [ bpl-test list ] [ bpl-test long ] [ bpl-test dict ] +[ bpl-test set ] [ bpl-test tuple ] [ bpl-test str ] [ bpl-test slice ] diff --git a/test/fabscript b/test/fabscript index 1989cc0d13..39586cb52e 100644 --- a/test/fabscript +++ b/test/fabscript @@ -81,6 +81,7 @@ for t in [('injected',), ('list',), ('long',), ('dict',), + ('set',), ('tuple',), ('str',), ('slice',), diff --git a/test/set.cpp b/test/set.cpp new file mode 100644 index 0000000000..e38c8ed78d --- /dev/null +++ b/test/set.cpp @@ -0,0 +1,79 @@ +// Copyright Fady Essam 2019. 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 +#define BOOST_ENABLE_ASSERT_HANDLER +#include + +#include +#include +#include +#include +#include +#include + +using namespace boost::python; + +object new_set() +{ + return set(); +} + +object data_set() +{ + set tmp1; + tmp1.add("value1"); + + tmp1.add(2); + return tmp1; +} + +object set_from_sequence(object sequence) +{ + return set(sequence); +} + + +void work_with_set(set data1) +{ + if (!data1.contains("k1")) { + std::cout << "data1 doesn't have k1" << std::endl; + } + data1.add("k1"); + + if (data1.contains("k1")) { + std::cout << "data1 now has k1" << std::endl; + } + + data1.discard("k1"); + if (!data1.contains("k1")) { + std::cout << "data1 doesn't have k1 again" << std::endl; + } + +} + +void test_templates(object print) +{ + std::string key = "key"; + + set tmp; + tmp.add("a test string"); + print(tmp); + tmp.add(13); + print(tmp.contains(1.5)); + print(tmp.contains(13)); + print(tmp); + + BOOST_ASSERT(tmp.__len__() == 2); +} + +BOOST_PYTHON_MODULE(set_ext) +{ + def("new_set", new_set); + def("data_set", data_set); + def("set_from_sequence", set_from_sequence); + def("work_with_set", work_with_set); + def("test_templates", test_templates); +} + +#include "module_tail.cpp" diff --git a/test/set.py b/test/set.py new file mode 100644 index 0000000000..3cd72ca050 --- /dev/null +++ b/test/set.py @@ -0,0 +1,34 @@ +# Copyright Fady Essam 2019. 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 __future__ import print_function +""" +>>> import set_ext +>>> set_ext.new_set() +set() +>>> set_ext.data_set() +{2, 'value1'} +>>> set_ext.set_from_sequence([1,2,3,3]) +{1, 2, 3} +>>> s = set_ext.new_set() +>>> set_ext.test_templates(print) +{'a test string'} +False +True +{'a test string', 13} +""" + +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)