// This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /// \file /// This file contains utility functions for registration of STL container /// classes. The methodology used is based on the SGI STL concepts. /// http://www.sgi.com/tech/stl/table_of_contents.html #ifndef CHAISCRIPT_BOOTSTRAP_STL_HPP_ #define CHAISCRIPT_BOOTSTRAP_STL_HPP_ #include #include #include #include #include #include "bootstrap.hpp" #include "boxed_value.hpp" #include "dispatchkit.hpp" #include "operators.hpp" #include "proxy_constructors.hpp" #include "register_function.hpp" #include "type_info.hpp" namespace chaiscript::bootstrap::standard_library { /// Bidir_Range, based on the D concept of ranges. /// \todo Update the Range code to base its capabilities on /// the user_typetraits of the iterator passed in template struct Bidir_Range { using container_type = Container; constexpr Bidir_Range(Container &c) : m_begin(c.begin()) , m_end(c.end()) { } constexpr bool empty() const noexcept { return m_begin == m_end; } constexpr void pop_front() { if (empty()) { throw std::range_error("Range empty"); } ++m_begin; } constexpr void pop_back() { if (empty()) { throw std::range_error("Range empty"); } --m_end; } constexpr decltype(auto) front() const { if (empty()) { throw std::range_error("Range empty"); } return (*m_begin); } constexpr decltype(auto) back() const { if (empty()) { throw std::range_error("Range empty"); } auto pos = m_end; --pos; return (*(pos)); } IterType m_begin; IterType m_end; }; namespace detail { template size_t count(const T &t_target, const typename T::key_type &t_key) { return t_target.count(t_key); } template void insert(T &t_target, const T &t_other) { t_target.insert(t_other.begin(), t_other.end()); } template void insert_ref(T &t_target, const typename T::value_type &t_val) { t_target.insert(t_val); } /// Add Bidir_Range support for the given ContainerType template void input_range_type_impl(const std::string &type, Module &m) { m.add(user_type(), type + "_Range"); copy_constructor(type + "_Range", m); m.add(constructor(), "range_internal"); m.add(fun(&Bidir_Type::empty), "empty"); m.add(fun(&Bidir_Type::pop_front), "pop_front"); m.add(fun(&Bidir_Type::front), "front"); m.add(fun(&Bidir_Type::pop_back), "pop_back"); m.add(fun(&Bidir_Type::back), "back"); } /// Algorithm for inserting at a specific position into a container template void insert_at(Type &container, int pos, const typename Type::value_type &v) { auto itr = container.begin(); auto end = container.end(); if (pos < 0 || std::distance(itr, end) < pos) { throw std::range_error("Cannot insert past end of range"); } std::advance(itr, pos); container.insert(itr, v); } /// Algorithm for erasing a specific position from a container template void erase_at(Type &container, int pos) { auto itr = container.begin(); auto end = container.end(); if (pos < 0 || std::distance(itr, end) <= pos) { throw std::range_error("Cannot erase past end of range"); } std::advance(itr, pos); container.erase(itr); } } // namespace detail template void input_range_type(const std::string &type, Module &m) { detail::input_range_type_impl>(type, m); detail::input_range_type_impl>("Const_" + type, m); } /// Add random_access_container concept to the given ContainerType /// http://www.sgi.com/tech/stl/RandomAccessContainer.html template void random_access_container_type(const std::string & /*type*/, Module &m) { // In the interest of runtime safety for the m, we prefer the at() method for [] access, // to throw an exception in an out of bounds condition. m.add(fun([](ContainerType &c, int index) -> typename ContainerType::reference { /// \todo we are preferring to keep the key as 'int' to avoid runtime conversions /// during dispatch. reevaluate return c.at(static_cast(index)); }), "[]"); m.add(fun([](const ContainerType &c, int index) -> typename ContainerType::const_reference { /// \todo we are preferring to keep the key as 'int' to avoid runtime conversions /// during dispatch. reevaluate return c.at(static_cast(index)); }), "[]"); } /// Add assignable concept to the given ContainerType /// http://www.sgi.com/tech/stl/Assignable.html template void assignable_type(const std::string &type, Module &m) { copy_constructor(type, m); operators::assign(m); } /// Add container resize concept to the given ContainerType /// http://www.cplusplus.com/reference/stl/ template void resizable_type(const std::string & /*type*/, Module &m) { m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) { return a->resize(n, val); }), "resize"); m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); }), "resize"); } /// Add container reserve concept to the given ContainerType /// http://www.cplusplus.com/reference/stl/ template void reservable_type(const std::string & /*type*/, Module &m) { m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->reserve(n); }), "reserve"); m.add(fun([](const ContainerType *a) { return a->capacity(); }), "capacity"); } /// Add container concept to the given ContainerType /// http://www.sgi.com/tech/stl/Container.html template void container_type(const std::string & /*type*/, Module &m) { m.add(fun([](const ContainerType *a) { return a->size(); }), "size"); m.add(fun([](const ContainerType *a) { return a->empty(); }), "empty"); m.add(fun([](ContainerType *a) { a->clear(); }), "clear"); } /// Add default constructable concept to the given Type /// http://www.sgi.com/tech/stl/DefaultConstructible.html template void default_constructible_type(const std::string &type, Module &m) { m.add(constructor(), type); } /// Add sequence concept to the given ContainerType /// http://www.sgi.com/tech/stl/Sequence.html template void sequence_type(const std::string & /*type*/, Module &m) { m.add(fun(&detail::insert_at), []() -> std::string { if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { return "insert_ref_at"; } else { return "insert_at"; } }()); m.add(fun(&detail::erase_at), "erase_at"); } /// Add back insertion sequence concept to the given ContainerType /// http://www.sgi.com/tech/stl/BackInsertionSequence.html template void back_insertion_sequence_type(const std::string &type, Module &m) { m.add(fun([](ContainerType &container) -> decltype(auto) { if (container.empty()) { throw std::range_error("Container empty"); } else { return (container.back()); } }), "back"); m.add(fun([](const ContainerType &container) -> decltype(auto) { if (container.empty()) { throw std::range_error("Container empty"); } else { return (container.back()); } }), "back"); using push_back = void (ContainerType::*)(const typename ContainerType::value_type &); m.add(fun(static_cast(&ContainerType::push_back)), [&]() -> std::string { if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { m.eval("# Pushes the second value onto the container while making a clone of the value\n" "def push_back(" + type + " container, x)\n" "{ \n" " if (x.is_var_return_value()) {\n" " x.reset_var_return_value() \n" " container.push_back_ref(x) \n" " } else { \n" " container.push_back_ref(clone(x)); \n" " }\n" "} \n"); return "push_back_ref"; } else { return "push_back"; } }()); m.add(fun(&ContainerType::pop_back), "pop_back"); } /// Front insertion sequence /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html template void front_insertion_sequence_type(const std::string &type, Module &m) { using push_ptr = void (ContainerType::*)(typename ContainerType::const_reference); using pop_ptr = void (ContainerType::*)(); m.add(fun([](ContainerType &container) -> decltype(auto) { if (container.empty()) { throw std::range_error("Container empty"); } else { return (container.front()); } }), "front"); m.add(fun([](const ContainerType &container) -> decltype(auto) { if (container.empty()) { throw std::range_error("Container empty"); } else { return (container.front()); } }), "front"); m.add(fun(static_cast(&ContainerType::push_front)), [&]() -> std::string { if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { m.eval("# Pushes the second value onto the front of container while making a clone of the value\n" "def push_front(" + type + " container, x)\n" "{ \n" " if (x.is_var_return_value()) {\n" " x.reset_var_return_value() \n" " container.push_front_ref(x) \n" " } else { \n" " container.push_front_ref(clone(x)); \n" " }\n" "} \n"); return "push_front_ref"; } else { return "push_front"; } }()); m.add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); } /// bootstrap a given PairType /// http://www.sgi.com/tech/stl/pair.html template void pair_type(const std::string &type, Module &m) { m.add(user_type(), type); m.add(fun(&PairType::first), "first"); m.add(fun(&PairType::second), "second"); basic_constructors(type, m); m.add(constructor(), type); } /// Add pair associative container concept to the given ContainerType /// http://www.sgi.com/tech/stl/PairAssociativeContainer.html template void pair_associative_container_type(const std::string &type, Module &m) { pair_type(type + "_Pair", m); } /// Add unique associative container concept to the given ContainerType /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html template void unique_associative_container_type(const std::string & /*type*/, Module &m) { m.add(fun(detail::count), "count"); using erase_ptr = size_t (ContainerType::*)(const typename ContainerType::key_type &); m.add(fun(static_cast(&ContainerType::erase)), "erase"); m.add(fun(&detail::insert), "insert"); m.add(fun(&detail::insert_ref), []() -> std::string { if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) { return "insert_ref"; } else { return "insert"; } }()); } /// Add a MapType container /// http://www.sgi.com/tech/stl/Map.html template void map_type(const std::string &type, Module &m) { m.add(user_type(), type); using elem_access = typename MapType::mapped_type &(MapType::*)(const typename MapType::key_type &); using const_elem_access = const typename MapType::mapped_type &(MapType::*)(const typename MapType::key_type &) const; m.add(fun(static_cast(&MapType::operator[])), "[]"); m.add(fun(static_cast(&MapType::at)), "at"); m.add(fun(static_cast(&MapType::at)), "at"); if (typeid(MapType) == typeid(std::map)) { m.eval(R"( def Map::`==`(Map rhs) { if ( rhs.size() != this.size() ) { return false; } else { auto r1 = range(this); auto r2 = range(rhs); while (!r1.empty()) { if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second)) { return false; } r1.pop_front(); r2.pop_front(); } true; } } )"); } container_type(type, m); default_constructible_type(type, m); assignable_type(type, m); unique_associative_container_type(type, m); pair_associative_container_type(type, m); input_range_type(type, m); } /// http://www.sgi.com/tech/stl/List.html template void list_type(const std::string &type, Module &m) { m.add(user_type(), type); front_insertion_sequence_type(type, m); back_insertion_sequence_type(type, m); sequence_type(type, m); resizable_type(type, m); container_type(type, m); default_constructible_type(type, m); assignable_type(type, m); input_range_type(type, m); } /// Create a vector type with associated concepts /// http://www.sgi.com/tech/stl/Vector.html template void vector_type(const std::string &type, Module &m) { m.add(user_type(), type); m.add(fun([](VectorType &container) -> decltype(auto) { if (container.empty()) { throw std::range_error("Container empty"); } else { return (container.front()); } }), "front"); m.add(fun([](const VectorType &container) -> decltype(auto) { if (container.empty()) { throw std::range_error("Container empty"); } else { return (container.front()); } }), "front"); back_insertion_sequence_type(type, m); sequence_type(type, m); random_access_container_type(type, m); resizable_type(type, m); reservable_type(type, m); container_type(type, m); default_constructible_type(type, m); assignable_type(type, m); input_range_type(type, m); if (typeid(VectorType) == typeid(std::vector)) { m.eval(R"( def Vector::`==`(Vector rhs) { if ( rhs.size() != this.size() ) { return false; } else { auto r1 = range(this); auto r2 = range(rhs); while (!r1.empty()) { if (!eq(r1.front(), r2.front())) { return false; } r1.pop_front(); r2.pop_front(); } true; } } )"); } } /// Add a String container /// http://www.sgi.com/tech/stl/basic_string.html template void string_type(const std::string &type, Module &m) { m.add(user_type(), type); operators::addition(m); operators::assign_sum(m); opers_comparison(m); random_access_container_type(type, m); sequence_type(type, m); default_constructible_type(type, m); // container_type(type, m); assignable_type(type, m); input_range_type(type, m); // Special case: add push_back to string (which doesn't support other back_insertion operations m.add(fun(&String::push_back), []() -> std::string { if (typeid(typename String::value_type) == typeid(Boxed_Value)) { return "push_back_ref"; } else { return "push_back"; } }()); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find(f, pos); }), "find"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); }), "rfind"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); }), "find_first_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); }), "find_last_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); }), "find_last_not_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); }), "find_first_not_of"); m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { return (*s += c); }), "+="); m.add(fun([](String *s) { s->clear(); }), "clear"); m.add(fun([](const String *s) { return s->empty(); }), "empty"); m.add(fun([](const String *s) { return s->size(); }), "size"); m.add(fun([](const String *s) { return s->c_str(); }), "c_str"); m.add(fun([](const String *s) { return s->data(); }), "data"); m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); }), "substr"); } /// Add a MapType container /// http://www.sgi.com/tech/stl/Map.html template void future_type(const std::string &type, Module &m) { m.add(user_type(), type); m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); m.add(fun([](FutureType &t) { return t.get(); }), "get"); m.add(fun(&FutureType::wait), "wait"); } } // namespace chaiscript::bootstrap::standard_library #endif