/* * moogpp/strutils.h * * Copyright (C) 2004-2016 by Yevgen Muntyan * * This file is part of medit. medit is free software; you can * redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the * Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public * License along with medit. If not, see . */ #pragma once #ifndef __cplusplus #error "This is a C++ header" #endif #include #include #include #include #include #include #include #include #include namespace g { class gstr; class gstrp; // Replacement for raw char* class gstrp : public gbuf { public: explicit gstrp(char* p = nullptr) : gbuf(p) {} gstrp(gstrp&& s) : gbuf(std::move(s)) {} gstrp& operator=(gstrp&& s) { static_cast&>(*this) = std::move(s); return *this; } char*& p() { return _get(); } char** pp() { return &_get(); } gstrp(const gstrp&) = delete; gstrp& operator=(const gstrp&) = delete; bool operator==(const char* p) const { return get() == p; } bool operator!=(const char* p) const { return get() != p; } }; struct printf_helper { template struct is_valid_arg { static const bool value = true; }; template struct is_valid_arg { static_assert(std::is_trivial::type>::value, "An object passed to a printf-like function"); static const bool value = std::is_trivial::type>::value && is_valid_arg::value; }; template static const T& transform_printf_arg (const T& arg); static const char* transform_printf_arg(const gstr& s); static const char* transform_printf_arg(const gstrp& s); // gcc seems to actually evaluate what's inside decltype(), and it can't cope with literals or va_list() static const char* dummy_format_string; static va_list dummy_va_list; template static auto call (const Func& func, const char* format, Args&& ...args) -> decltype(func(dummy_format_string)) { g_assert (printf_helper::is_valid_arg (args)))...>::value); return func(format, printf_helper::transform_printf_arg (std::forward (args))...); } template static auto callv (const Func& func, const char* format, Args&& ...args) -> decltype(func(dummy_format_string, dummy_va_list)) { g_assert (printf_helper::is_valid_arg (args)))...>::value); return call_helper (func, format, printf_helper::transform_printf_arg (std::forward (args))...); } template static void call_void (const Func& func, const char* format, Args&& ...args) { g_assert (printf_helper::is_valid_arg (args)))...>::value); func (format, printf_helper::transform_printf_arg (std::forward (args))...); } template static void callv_void (const Func& func, const char* format, Args&& ...args) { g_assert (printf_helper::is_valid_arg (args)))...>::value); call_helper_void (func, format, printf_helper::transform_printf_arg (std::forward (args))...); } private: template static auto call_helper (const Func& func, const char* format, ...) -> decltype(func(dummy_format_string, dummy_va_list)) { va_list args; va_start(args, format); auto ret = func(format, args); va_end(args); return ret; } template static void call_helper_void (const Func& func, const char* format, ...) { va_list args; va_start (args, format); func (format, args); va_end (args); } }; class gstr { public: gstr(); ~gstr(); gstr(const char* s, mem_transfer mt); gstr(gstrp&& s); static const gstr null; gstr(const gstr&); gstr& operator=(const gstr&); gstr(gstr&&); gstr& operator=(gstr&&); gstr& operator=(gstrp&&); gstr(nullptr_t) : gstr() {} gstr& operator=(nullptr_t) { clear(); return *this; } gstr(const gstr&, mem_transfer) = delete; void set(const gstr& s) = delete; void set_const(const gstr& s) = delete; void set_new(const gstr& s) = delete; static gstr wrap(const gstr& s) = delete; static gstr wrap_const(const gstr& s) = delete; static gstr wrap_new(const gstr& s) = delete; void set(const char *s) { assign(s, mem_transfer::make_copy); } void set_new(char *s) { assign(s, mem_transfer::take_ownership); } void set_const(const char *s) { assign(s, mem_transfer::borrow); } static gstr wrap(const char *s) { return gstr(s, mem_transfer::make_copy); } static gstr wrap_new(char *s) { return gstr(s, mem_transfer::take_ownership); } static gstr wrap_const(const char *s) { return gstr(s, mem_transfer::borrow); } bool is_null() const; operator const char*() const; const char* get() const { return static_cast(*this); } const char* get_non_null() const { return is_null() ? "" : get(); } char* get_mutable(); char* release_owned(); void clear(); void reset() { clear(); } char *strdup() const { return g_strdup(*this); } bool empty() const { const char* s = *this; return !s || !*s; } // These must not be called, to avoid ambiguity between an empty string and null operator bool() const = delete; bool operator!() const = delete; static gstr vprintf(const char* format, va_list args) G_GNUC_PRINTF(1, 0); template static gstr printf(const char* format, Args&& ...args) { return wrap_new (printf_helper::callv (g_strdup_vprintf, format, std::forward (args)...)); } template void set_printf (const char* format, Args&& ...args) { set_new (printf_helper::callv (g_strdup_vprintf, format, std::forward (args)...)); } private: void assign(const char* s, mem_transfer mt); private: void *m_p; // either char* or Data* bool m_is_inline; bool m_is_const; }; template inline const T& printf_helper::transform_printf_arg (const T& arg) { g_assert (is_valid_arg::value); return arg; } inline const char* printf_helper::transform_printf_arg(const gstr& s) { return s.get (); } inline const char* printf_helper::transform_printf_arg(const gstrp& s) { return s.get (); } using gstrvec = std::vector; using gstrset = std::unordered_set; using gstrmap = std::unordered_map; bool operator==(const gstr& s1, const char* s2); bool operator==(const char* s1, const gstr& s2); bool operator==(const gstr& s1, const gstr& s2); bool operator!=(const gstr& s1, const char* s2); bool operator!=(const char* s1, const gstr& s2); bool operator!=(const gstr& s1, const gstr& s2); bool operator==(const gstr&, nullptr_t) = delete; bool operator==(nullptr_t, const gstr&) = delete; class strv { public: strv(char** p = nullptr) : m_p(p) {} ~strv() { ::g_strfreev(m_p); } void set(char** p) { if (m_p != p) { ::g_strfreev(m_p); m_p = p; } } void reset(char** p = nullptr) { set(p); } char** get() const { return m_p; } static strv convert(gstrvec v); gsize size() const { return m_p ? g_strv_length (m_p) : 0; } operator char**() const = delete; char*** operator&() = delete; char** release() { char** p = m_p; m_p = nullptr; return p; } const char* operator[] (gsize i) const { return m_p[i]; } strv(const strv&) = delete; strv& operator=(const strv&) = delete; strv(strv&& other) : strv() { *this = std::move(other); } strv& operator=(strv&& other) { std::swap(m_p, other.m_p); return *this; } strv& operator=(char** p) { set(p); return *this; } bool operator==(nullptr_t) const { return m_p == nullptr; } bool operator!=(nullptr_t) const { return m_p != nullptr; } operator bool() const { return m_p != nullptr; } bool operator !() const { return m_p == nullptr; } private: char** m_p; }; gstrvec convert(strv v); class gerrp { public: explicit gerrp(GError** errp = nullptr) : m_errp(errp ? errp : &m_local), m_local(nullptr) {} ~gerrp() { if (m_errp != &m_local) clear(); } operator bool() const { return (*m_errp) != nullptr; } bool operator!() const { return (*m_errp) == nullptr; } GError* get() const { return (*m_errp); } GError* operator->() const { return (*m_errp); } GError** operator&() { return m_errp; } //void propagate(GError** dest) { g_propagate_error(dest, m_err); m_err = nullptr; } void clear() { if (*m_errp) g_error_free(*m_errp); *m_errp = nullptr; m_local = nullptr; } gerrp(const gerrp&) = delete; gerrp& operator=(const gerrp&) = delete; gerrp(gerrp&& other) = delete; gerrp& operator=(gerrp&& other) { clear(); if (other) g_propagate_error (m_errp, other.get()); other.m_errp = &other.m_local; other.m_local = nullptr; return *this; } private: GError** m_errp; GError* m_local; }; class strbuilder { public: strbuilder(const char *init = nullptr, gssize len = -1); strbuilder(gsize reserve); ~strbuilder(); gstr release(); const char* get() const; strbuilder(const strbuilder&) = delete; strbuilder& operator=(const strbuilder&) = delete; void truncate(gsize len); void set_size(gsize len); void append(const char* val, gssize len = -1); void append(const gstrp& val); void append(char c); void append(gunichar wc); void prepend(const char* val, gssize len = -1); void prepend(const gstrp& val); void prepend(char c); void prepend(gunichar wc); void insert(gssize pos, const char* val, gssize len = -1); void insert(gssize pos, const gstrp& val); void insert(gssize pos, char c); void insert(gssize pos, gunichar wc); void overwrite(gsize pos, const char* val, gssize len = -1); void erase(gssize pos = 0, gssize len = -1); void ascii_down(); void ascii_up(); void vprintf(const char* format, va_list args); void append_vprintf(const char* format, va_list args); void append_uri_escaped(const char* unescaped, const char* reserved_chars_allowed = nullptr, bool allow_utf8 = false); template void printf (const char* format, Args&& ...args) { g_return_if_fail (m_buf); printf_helper::callv_void ([&] (const char* format, va_list args) { g_string_vprintf (m_buf, format, args); }, format, std::forward (args)...); } template void append_printf (const char* format, Args&& ...args) { g_return_if_fail (m_buf); printf_helper::callv_void ([&] (const char* format, va_list args) { g_string_append_vprintf (m_buf, format, args); }, format, std::forward(args)...); } private: mutable GString* m_buf; mutable gstrp m_result; }; //gstrvec convert(strv v); void g_free(const gstr&) = delete; void g_free(const gstrp&) = delete; void g_free(const strv&) = delete; void g_strfreev(const strv&) = delete; } // namespace g namespace std { template<> struct hash<::g::gstr> { size_t operator()(const ::g::gstr& s) const; }; } // namespace std template inline void moo_g_print(const char* format, Args&& ...args) { static_assert(g::printf_helper::is_valid_arg::value, "Passed an object to g_strdup_printf"); g_print (format, std::forward (args)...); } template inline char* moo_g_strdup_printf (const char* format, Args&& ...args) { static_assert(g::printf_helper::is_valid_arg::value, "Passed an object to g_strdup_printf"); return g_strdup_printf (format, std::forward (args)...); } #undef g_strdup_printf #undef g_print #define g_strdup_printf moo_g_strdup_printf #define g_print moo_g_print