// This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) // Copyright 2009-2017, 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 { namespace bootstrap { namespace 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 { typedef Container container_type; Bidir_Range(Container &c) : m_begin(c.begin()), m_end(c.end()) { } bool empty() const { return m_begin == m_end; } void pop_front() { if (empty()) { throw std::range_error("Range empty"); } ++m_begin; } void pop_back() { if (empty()) { throw std::range_error("Range empty"); } --m_end; } decltype(auto) front() const { if (empty()) { throw std::range_error("Range empty"); } return (*m_begin); } 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-1)) { throw std::range_error("Cannot erase past end of range"); } std::advance(itr, pos); container.erase(itr); } } 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); } template ModulePtr input_range_type(const std::string &type) { auto m = std::make_shared(); input_range_type(type, *m); return 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 prefering 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 prefering to keep the key as 'int' to avoid runtime conversions /// during dispatch. reevaluate return c.at(static_cast(index)); }), "[]"); } template ModulePtr random_access_container_type(const std::string &type) { auto m = std::make_shared(); random_access_container_type(type, *m); return m; } /// 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); } template ModulePtr assignable_type(const std::string &type) { auto m = std::make_shared(); assignable_type(type, *m); return 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"); } template ModulePtr resizable_type(const std::string &type) { auto m = std::make_shared(); resizable_type(type, *m); return m; } /// 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"); } template ModulePtr reservable_type(const std::string &type) { auto m = std::make_shared(); reservable_type(type, *m); return m; } /// 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"); } template ModulePtr container_type(const std::string& type) { auto m = std::make_shared(); container_type(type, *m); return m; } /// 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); } template ModulePtr default_constructible_type(const std::string& type) { auto m = std::make_shared(); default_constructible_type(type, *m); return m; } /// 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"); } template ModulePtr sequence_type(const std::string &type) { auto m = std::make_shared(); sequence_type(type, *m); return m; } /// 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) { typedef typename ContainerType::reference (ContainerType::*backptr)(); m.add(fun(static_cast(&ContainerType::back)), "back"); typedef void (ContainerType::*push_back)(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"); } template ModulePtr back_insertion_sequence_type(const std::string &type) { auto m = std::make_shared(); back_insertion_sequence_type(type, *m); return m; } /// Front insertion sequence /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html template void front_insertion_sequence_type(const std::string &type, Module& m) { typedef typename ContainerType::reference (ContainerType::*front_ptr)(); typedef typename ContainerType::const_reference (ContainerType::*const_front_ptr)() const; typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference); typedef void (ContainerType::*pop_ptr)(); m.add(fun(static_cast(&ContainerType::front)), "front"); m.add(fun(static_cast(&ContainerType::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"); } template ModulePtr front_insertion_sequence_type(const std::string &type) { auto m = std::make_shared(); front_insertion_sequence_type(type, *m); return m; } /// 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); } template ModulePtr pair_type(const std::string &type) { auto m = std::make_shared(); pair_type(type, *m); return m; } /// 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); } template ModulePtr pair_associative_container_type(const std::string &type) { auto m = std::make_shared(); pair_associative_container_type(type, *m); return 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"); typedef size_t (ContainerType::*erase_ptr)(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"; } }()); } template ModulePtr unique_associative_container_type(const std::string &type) { auto m = std::make_shared(); unique_associative_container_type(type, *m); return m; } /// 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); typedef typename MapType::mapped_type &(MapType::*elem_access)(const typename MapType::key_type &); typedef const typename MapType::mapped_type &(MapType::*const_elem_access)(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); } template ModulePtr map_type(const std::string &type) { auto m = std::make_shared(); map_type(type, *m); return 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); } template ModulePtr list_type(const std::string &type) { auto m = std::make_shared(); list_type(type, m); return 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); typedef typename VectorType::reference (VectorType::*frontptr)(); typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const; m.add(fun(static_cast(&VectorType::front)), "front"); m.add(fun(static_cast(&VectorType::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; } } )" ); } } template ModulePtr vector_type(const std::string &type) { auto m = std::make_shared(); vector_type(type, *m); return m; } /// 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) { 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"); } template ModulePtr string_type(const std::string &type) { auto m = std::make_shared(); string_type(type, *m); return m; } /// 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::get), "get"); m.add(fun(&FutureType::wait), "wait"); } template ModulePtr future_type(const std::string &type) { auto m = std::make_shared(); future_type(type, *m); return m; } } } } #endif