/* * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, as * published by the Free Software Foundation. * * This program is also distributed with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an * additional permission to link the program and your derivative works * with the separately licensed software that they have included with * MySQL. * * Without limiting anything contained in the foregoing, this file, * which is part of MySQL Connector/C++, is also subject to the * Universal FOSS Exception, version 1.0, a copy of which can be found at * http://oss.oracle.com/licenses/universal-foss-exception. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYSQLX_COMMON_H #define MYSQLX_COMMON_H #include "../common.h" PUSH_SYS_WARNINGS #include #include #include #include #include #include // for memcpy #include // std::move etc POP_SYS_WARNINGS #define CATCH_AND_WRAP \ catch (const ::mysqlx::Error&) { throw; } \ catch (const std::out_of_range&) { throw; } \ catch (const std::exception &e) \ { throw ::mysqlx::Error(e.what()); } \ catch (const char *e) \ { throw ::mysqlx::Error(e); } \ catch (...) \ { throw ::mysqlx::Error("Unknown exception"); } \ namespace mysqlx { MYSQLX_ABI_BEGIN(2,0) using std::out_of_range; using common::byte; class Value; /** Base class for connector errors. @internal TODO: Derive from std::system_error and introduce proper error codes. @endinternal @ingroup devapi */ // TODO: Make it header-only class somehow... DLL_WARNINGS_PUSH class PUBLIC_API Error : public common::Error { DLL_WARNINGS_POP public: Error(const char *msg) : common::Error(msg) {} }; inline void throw_error(const char *msg) { throw Error(msg); } /** A wrapper around std::wstring that can perform conversions from/to different character encodings used by MySQL. Currently only utf-8 encoding is supported. @ingroup devapi_aux */ class string : public std::u16string { struct Impl { PUBLIC_API static std::string to_utf8(const string&); PUBLIC_API static void from_utf8(string&, const std::string&); PUBLIC_API static std::u32string to_ucs4(const string&); PUBLIC_API static void from_ucs4(string&, const std::u32string&); PUBLIC_API static std::wstring to_wide(const string&); PUBLIC_API static void from_wide(string&, const std::wstring&); }; template struct traits {}; public: string() {} string(const string&) = default; string(string&&) = default; string& operator=(const string&) = default; string& operator=(string&&) = default; using std::u16string::basic_string; string(const std::u16string &other) : std::u16string(other) {} string(std::u16string &&other) : std::u16string(std::move(other)) {} template string(const C *other) { try { if (!other) return; std::basic_string str(other); traits::from_str(*this, str); } CATCH_AND_WRAP } template string(const std::basic_string &other) { try { traits::from_str(*this, other); } CATCH_AND_WRAP } template operator std::basic_string() const { try { return traits::to_str(*this); } CATCH_AND_WRAP } friend bool operator==(const string&lhs, const string&rhs) { return operator==((const std::u16string&)lhs, (const std::u16string&)rhs); } friend bool operator!=(const string&lhs, const string&rhs) { return !(lhs == rhs); } // Note: These are needed to help overload resolution :/ friend bool operator==(const string &lhs, const char16_t *rhs) { return lhs == string(rhs); } friend bool operator==(const char16_t *lhs, const string &rhs) { return string(lhs) == rhs; } friend bool operator!=(const string &lhs, const char16_t *rhs) { return !(lhs == rhs); } friend bool operator!=(const char16_t *lhs, const string &rhs) { return !(lhs == rhs); } }; template<> struct string::traits { using string = std::string; static void from_str(mysqlx::string &to, const string &from) { Impl::from_utf8(to, from); } static string to_str(const mysqlx::string &from) { return Impl::to_utf8(from); } }; template<> struct string::traits { using string = std::wstring; static void from_str(mysqlx::string &to, const string &from) { Impl::from_wide(to, from); } static string to_str(const mysqlx::string &from) { return Impl::to_wide(from); } }; template<> struct string::traits { using string = std::u32string; static void from_str(mysqlx::string &to, const string &from) { Impl::from_ucs4(to, from); } static string to_str(const mysqlx::string &from) { return Impl::to_ucs4(from); } }; inline std::ostream& operator<<(std::ostream &out, const string &str) { const std::string utf8(str); out << utf8; return out; } typedef unsigned long col_count_t; typedef unsigned long row_count_t; /** Class representing a region of memory holding raw bytes. Method `begin()` returns pointer to the first byte in the region, `end()` to one past the last byte in the region. @note An instance of `bytes` does not store the bytes - it merely describes a region of memory and is equivalent to a pair of pointers. It is very cheap to copy `bytes` and pass them by value. @note This class extends std::pair to make it consistent with how memory regions are described by std::get_temporary_buffer(). It is also possible to initialize a `bytes` instance by buffer returned from std::get_temporary_buffer(), as follows: bytes buf = std::get_temporary_buffer(size); @ingroup devapi_aux */ class bytes : public std::pair { public: bytes(const byte *beg_, const byte *end_) : pair(beg_, end_ - beg_) {} bytes(const byte *beg, size_t len) : pair(beg, len) {} bytes(const char *str) : pair((const byte*)str, 0) { if (nullptr != str) second = strlen(str); } bytes(std::pair buf) : pair(buf) {} bytes() : pair(nullptr, 0) {} bytes(const bytes &) = default; virtual const byte* begin() const { return first; } virtual const byte* end() const { return first + second; } size_t length() const { return second; } size_t size() const { return length(); } class Access; friend Access; }; /* Infrastructure for type-agnostic handling of lists ================================================== Template internal::List_initializer<> defined below is used to return lists of values from public API method so that user can store this list in a container of his choice. The only requirement is that the container instance should be constructible from two iterators defining a range of elements (such constructors exists for standard STL containers, for example). Thus, given a public API method foo() which returns a List_initializer<> for lists of elements of type X, user can do the following: My_container cont = foo(); The container will be constructed as if this code was executed: My_container cont = My_container(begin, end); where begin and end are STL iterators defining a range of elements of type X. This is implemented by defining templated conversion operator. Apart from initializing containers, values of List_initializer<> type can be iterated using a range loop: for(X &el : foo()) { ... } Otherwise, user should not be able to use List_initializer<> values directly. */ namespace internal { /* Iterator template. It defines an STL input iterator which is implemented using an implementation object of some type Impl. It is assumed that Impl has the following methods: void iterator_start() - puts iterator in "before begin" position; bool iterator_next() - moves iterator to next position, returns false if it was not possible; Value_type iterator_get() - gets current value. An implementation object must be passed to iterator constructor. Iterator stores only a pointer to this implementation (so it must exist as long as iterator is used). */ template< typename Impl, typename T = typename std::iterator_traits::value_type, typename Distance = typename std::iterator_traits::difference_type, typename Pointer = typename std::iterator_traits::pointer, typename Reference = typename std::iterator_traits::reference > struct Iterator : std::iterator < std::input_iterator_tag, T, Distance, Pointer, Reference > { protected: typename std::remove_reference::type *m_impl = NULL; bool m_at_end = false; public: Iterator(Impl& impl) : m_impl(&impl) { m_impl->iterator_start(); m_at_end = !m_impl->iterator_next(); } Iterator() : m_at_end(true) {} bool operator==(const Iterator &other) const { return (m_at_end && other.m_at_end); } bool operator !=(const Iterator &other) const { /* Compares only if both iterators are at the end of the sequence. */ return !(m_at_end && other.m_at_end); } Iterator& operator++() { try { if (m_impl && !m_at_end) m_at_end = !m_impl->iterator_next(); return *this; } CATCH_AND_WRAP } T operator*() const { if (!m_impl || m_at_end) THROW("Attempt to dereference null iterator"); try { return m_impl->iterator_get(); } CATCH_AND_WRAP } friend Impl; }; /* List_initializer object can be used to initialize a container of arbitrary type U with list of items taken from a source object. It is assumed that the source object type Source defines iterator type and that std::begin/end() return iterators to the beginning and end of the sequence. The container type U is assumed to have a constructor from begin/end iterator. List_iterator defines begin/end() methods, so it is possible to iterate over the sequence without storing it in any container. */ template class List_initializer { protected: Source m_src; friend Source; public: /* Arguments given to the constructor are passed to the internal m_src object. */ template List_initializer(Ty&&... args) : m_src(std::forward(args)...) {} /* Narrow the set of types for which this template is instantiated to avoid ambiguous conversion errors. It is important to disallow conversion to std::initializer_list<> because this conversion path is considered when assigning to STL containers. */ template < typename U , typename std::enable_if< !std::is_same< U, std::initializer_list >::value >::type* = nullptr > operator U() { try { return U(std::begin(m_src), std::end(m_src)); } CATCH_AND_WRAP } auto begin() -> decltype(std::begin(m_src)) { try { return std::begin(m_src); } CATCH_AND_WRAP } auto end() const -> decltype(std::end(m_src)) { try { return std::end(m_src); } CATCH_AND_WRAP } }; template struct iterator_traits { using value_type = typename std::remove_reference::type; using difference_type = typename std::iterator_traits::difference_type; using pointer = typename std::iterator_traits::pointer; using reference = typename std::iterator_traits::reference; }; /* This helper template adapts class Impl to be used as a source for List_initializer<> template. Class Impl should be suitable for the Iterator<> template which is used to build iterators required by List_initializer<>. That is, Impl should implement iterator_start(), iteratore_next() etc (see Iterator<>). */ template< typename Impl, typename Value_type = typename Impl::Value, typename Distance = typename iterator_traits::difference_type, typename Pointer = typename iterator_traits::pointer, typename Reference = typename iterator_traits::reference > class List_source { protected: Impl m_impl; public: template List_source(Ty&&... args) : m_impl(std::forward(args)...) {} using iterator = Iterator; iterator begin() { return iterator(m_impl); } iterator end() const { return iterator(); } }; /* A template used to adapt an object of class Impl that represents an array of values accessed via operator[] to be used as source for List_initializer<> template. This template uses instance of Impl to implement the iterator methods iterator_start(), so that it can be used with Iterator<> template. */ template class Array_src_impl { protected: Impl m_impl; size_t m_pos = 0; bool m_at_begin = true; public: template Array_src_impl(Ty&&... args) : m_impl(std::forward(args)...) {} void iterator_start() { m_pos = 0; m_at_begin = true; } bool iterator_next() { if (m_at_begin) m_at_begin = false; else m_pos++; return m_pos < size(); } Value_type iterator_get() { return operator[](m_pos); } Value_type operator[](size_t pos) { return m_impl[pos]; } size_t size() const { return m_impl.size(); } }; /* This template adapts an object of type Impl holding an array of values as a source for List_initializer<> template. It combines List_source<> and Array_src_impl<> adapters. */ template< typename Impl, typename Value_type = typename Impl::Value, typename Distance = typename iterator_traits::difference_type, typename Pointer = typename iterator_traits::pointer, typename Reference = typename iterator_traits::reference > class Array_source : public List_source< Array_src_impl, Value_type, Distance, Pointer, Reference > { using Base = List_source< Array_src_impl, Value_type, Distance, Pointer, Reference >; using Base::m_impl; public: using List_source< Array_src_impl, Value_type, Distance, Pointer, Reference >::List_source; Value_type operator[](size_t pos) { return m_impl[pos]; } size_t size() const { return m_impl.size(); } }; } // internal /* Infrastructure for handling variable argument lists =================================================== See documentation of Args_processor<> template. */ namespace internal { /* Type trait which checks if std::begin()/end() work on objects of given class C, so that it can be used as a range to iterate over. TODO: Make it work also with user-defined begin()/end() functions. TODO: Make it work with plain C arrays. For example: int vals[] = { 1, 2, 3 } process_args(data, vals) */ template class is_range { /* Note: This overload is taken into account only if std::begin(X) and std::end(X) expressions are valid. */ template static std::true_type test( decltype(std::begin(*((X*)nullptr)))*, decltype(std::end(*((X*)nullptr)))* ); template static std::false_type test(...); public: static const bool value = std::is_same< std::true_type, decltype(test(nullptr, nullptr)) >::value; }; /* Class template to be used for uniform processing of variable argument lists in public API methods. This template handles the cases where arguments are specified directly as a list: method(arg1, arg2, ..., argN) or they are taken from a container such as std::list: method(container) or they are taken from a range of items described by two iterators: method(begin, end) A class B that is using this template to define a varargs method 'foo' should define it as follows: template X foo(T... args) { Args_processor::process_args(m_impl, args...); return ...; } Process_args() is a static method of Args_processor<> and therefore additional context data is passed to it as the first argument. By default this context is a pointer to internal implementation object, as defined by the base class B. The process_args() methods does all the necessary processing of the variable argument list, passing the resulting items one-by-one to B::process_one() method. Base class B must define this static method, which takes the context and one data item as arguments. B::process_one() method can have overloads that handle different types of data items. See devapi/detail/crud.h for usage examples. */ template class Args_processor { public: /* Check if item of type T can be passed to Base::process_one() */ template class can_process { template static std::true_type test(decltype(Base::process_one(*(D*)nullptr, *(X*)nullptr))*); template static std::false_type test(...); public: static const bool value = std::is_same< std::true_type, decltype(test(nullptr)) >::value; }; public: /* Process items from a container. */ template < typename C, typename std::enable_if::value>::type* = nullptr, typename std::enable_if::value>::type* = nullptr > static void process_args(D data, C container) { // TODO: use (const) reference to avoid copying instances? for (auto el : container) { Base::process_one(data, el); } } /* If process_args(data, a, b) is called and a,b are of the same type It which can not be passed to Base::process_one() then we assume that a and b are iterators that describe a range of elements to process. */ template < typename It, typename std::enable_if::value>::type* = nullptr > static void process_args(D data, const It &begin, const It &end) { for (It it = begin; it != end; ++it) { Base::process_one(data, *it); } } /* Process elements given as a varargs list. */ template < typename T, typename... R, typename std::enable_if::value>::type* = nullptr > static void process_args(D data, T first, R&&... rest) { process_args1(data, first, std::forward(rest)...); } private: template < typename T, typename... R, typename std::enable_if::value>::type* = nullptr > static void process_args1(D data, T first, R&&... rest) { Base::process_one(data, first); process_args1(data, std::forward(rest)...); } static void process_args1(D) {} }; } // internal namespace MYSQLX_ABI_END(2,0) } // mysqlx #endif