#ifndef GI_BASE_HPP #define GI_BASE_HPP // attempt to auto-discover exception support: #ifndef GI_CONFIG_EXCEPTIONS #if defined(_MSC_VER) #include // for _HAS_EXCEPTIONS #endif #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) #define GI_CONFIG_EXCEPTIONS 1 #else #define GI_CONFIG_EXCEPTIONS 0 #endif #endif // lots of declarations might be attributed as deprecated, // but not so annotated, so let's avoid warning floods // also handle complaints about const qualified casts // (due to silly const qualified scalar parameters) #define GI_DISABLE_DEPRECATED_WARN_BEGIN \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wignored-qualifiers\"") #define GI_DISABLE_DEPRECATED_WARN_END \ _Pragma("GCC diagnostic pop") _Pragma("GCC diagnostic pop") #include "boxed.hpp" #include "objectbase.hpp" #include #include #include #include // required for generated code #include #if GI_DL #include #include #endif namespace gi { namespace detail { template [[noreturn]] inline void try_throw(E &&e) { #if GI_CONFIG_EXCEPTIONS throw std::forward(e); #else (void)e; abort(); #endif } // constructor does not appreciate NULL, so wrap that here // map NULL to empty string; not quite the same, but it will do inline std::string make_string(const char *s) { return std::string(s ? s : ""); } // helper string subtype // used to overload unwrap of optional string argument // (transfrom empty string to null) // NOTE std::optional requires C++17 class optional_string : public std::string {}; class noncopyable { public: noncopyable() {} noncopyable(const noncopyable &) = delete; noncopyable &operator=(const noncopyable &) = delete; noncopyable(noncopyable &&) = default; noncopyable &operator=(noncopyable &&) = default; }; class scope_guard : public noncopyable { private: std::function cleanup_; public: scope_guard(std::function &&cleanup) : cleanup_(std::move(cleanup)) {} ~scope_guard() noexcept(false) { #if GI_CONFIG_EXCEPTIONS #if __cplusplus >= 201703L auto pending = std::uncaught_exceptions(); #else auto pending = std::uncaught_exception(); #endif try { #endif cleanup_(); #if GI_CONFIG_EXCEPTIONS } catch (...) { if (!pending) throw; } #endif } }; // as in // http://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/ template using disable_if_same_or_derived = typename std::enable_if< !std::is_base_of::type>::value>::type; } // namespace detail namespace repository { // class types declare c type within class // others can do so using this (e.g. enum) template struct declare_ctype_of {}; // and for all cases the reverse cpp type template struct declare_cpptype_of {}; // generate code must specialize appropriately template struct is_enumeration : public std::false_type {}; template struct is_bitfield : public std::false_type {}; } // namespace repository struct transfer_full_t; struct transfer_none_t; namespace traits { template struct if_valid_type { typedef U type; }; template struct is_valid_type : public std::true_type {}; template struct is_type_complete : public std::false_type {}; template struct is_type_complete::type> : public std::true_type {}; template using is_decayed = std::is_same::type, T>; template using is_cboxed = typename std::conditional::value, std::true_type, std::false_type>::type; template using is_gboxed = typename std::conditional::value, std::true_type, std::false_type>::type; template using is_boxed = typename std::conditional::value, std::true_type, std::false_type>::type; // avoid derived cases template using is_object = typename std::conditional::value && sizeof(T) == sizeof(gpointer), std::true_type, std::false_type>::type; template using is_wrapper = typename std::conditional::value && sizeof(T) == sizeof(gpointer), std::true_type, std::false_type>::type; // bring in to this namespace using repository::is_bitfield; using repository::is_enumeration; // aka passthrough template using is_basic = typename std::conditional::value || std::is_same::value || std::is_arithmetic::value, std::true_type, std::false_type>::type; // almost passthrough (on lower level at least) template using is_plain = typename std::conditional::value || std::is_enum::value, std::true_type, std::false_type>::type; template struct is_reftype : public std::false_type {}; template struct is_reftype::type::BoxType>::type> : public std::true_type {}; template struct has_ctype_member : public std::false_type {}; template struct has_ctype_member::type> : public std::true_type {}; // return corresponding c type (if any) // (string and basic type not considered) // preserve const template struct ctype {}; // class case template struct ctype::type::BaseObjectType>::value>::type> { typedef typename std::remove_reference::type CppType; // make sure; avoid subclassed cases static_assert(is_wrapper::value || is_boxed::value, "must be object or boxed wrapper"); typedef typename CppType::BaseObjectType CType; typedef typename std::conditional::value, const CType, CType>::type *type; }; // remaining cases template struct ctype::type>::type> { typedef typename repository::declare_ctype_of::type CType; typedef typename std::conditional::value, const CType, CType>::type type; }; // basic cases passthrough template struct ctype::value && !std::is_same::value) || std::is_same::value || std::is_same::value>::type> { typedef T type; }; // ... exception though for bool template<> struct ctype { typedef gboolean type; }; // as used in callback signatures // or in list (un)wrapping template<> struct ctype { typedef const char *type; }; template<> struct ctype { typedef char *type; }; template struct ctype> { typedef std::pair::type, typename ctype::type> type; }; // conversely // return corresponding cpp type (if known) // (string and basic type not considered) // preserve const template struct cpptype {}; // generic template struct cpptype::type>::type>::type> { typedef typename repository::declare_cpptype_of< typename std::remove_const::type>::type CppType; typedef typename std::conditional::value, const CppType, CppType>::type type; }; template struct cpptype::type>::type>::type> { typedef typename repository::declare_cpptype_of< typename std::remove_const::type>::type CppType; typedef typename std::conditional::value, const CppType, CppType>::type type; }; // basic cases passthrough template struct cpptype::value || std::is_same::value || std::is_same::value>::type> { typedef T type; }; #if 0 template<> struct cpptype { using type = std::string; }; #endif // handle none transfer case template struct cpptype { using CppType = typename cpptype::type; template struct map_type { using type = TT; }; template struct map_type::type> { using type = typename TT::ReferenceType; }; using type = typename map_type::type; }; // map owning box type to corresponding reference box type template struct reftype { typedef typename T::ReferenceType type; }; } // namespace traits // specify transfer type when (un)wrapping // this approach is safer than some booleans and allows overload combinations struct transfer_t { const int value; constexpr explicit transfer_t(int v = 0) : value(v) {} }; struct transfer_none_t : public transfer_t { constexpr transfer_none_t() : transfer_t(0) {} }; struct transfer_full_t : public transfer_t { constexpr transfer_full_t() : transfer_t(1) {} }; struct transfer_container_t : public transfer_t { constexpr transfer_container_t() : transfer_t(2) {} }; const constexpr transfer_t transfer_dummy = transfer_t(); const constexpr transfer_none_t transfer_none = transfer_none_t(); const constexpr transfer_full_t transfer_full = transfer_full_t(); const constexpr transfer_container_t transfer_container = transfer_container_t(); template struct element_transfer {}; template<> struct element_transfer { typedef transfer_none_t type; }; template<> struct element_transfer { typedef transfer_full_t type; }; template<> struct element_transfer { typedef transfer_none_t type; }; // unwrapping a callback // specify call scope type struct scope_t { const int value; constexpr explicit scope_t(int v = 0) : value(v) {} }; struct scope_call_t : public scope_t { constexpr scope_call_t() : scope_t(0) {} }; struct scope_async_t : public scope_t { constexpr scope_async_t() : scope_t(1) {} }; struct scope_notified_t : public scope_t { constexpr scope_notified_t() : scope_t(2) {} }; const constexpr scope_t scope_dummy = scope_t(); const constexpr scope_call_t scope_call = scope_call_t(); const constexpr scope_async_t scope_async = scope_async_t(); const constexpr scope_notified_t scope_notified = scope_notified_t(); // (dummy) helper tag to aid in overload resolution template struct interface_tag { typedef Interface type; }; #if GI_DL namespace detail { // dynamic load of symbol inline void * load_symbol(const std::vector libs, const char *symbol) { void *s = nullptr; for (const auto &l : libs) { auto h = dlopen(l, RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE); if (h) { s = dlsym(h, symbol); dlclose(h); if (s) break; } } return s; } } // namespace detail #endif // GI_DL } // namespace gi #endif // GI_BASE_HPP