416 lines
12 KiB
C++
416 lines
12 KiB
C++
/*
|
|
* moogpp/strutils.h
|
|
*
|
|
* Copyright (C) 2004-2016 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#ifndef __cplusplus
|
|
#error "This is a C++ header"
|
|
#endif
|
|
|
|
#include <moogpp/memutils.h>
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <functional>
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
#include <unordered_map>
|
|
#include <type_traits>
|
|
#include <stdarg.h>
|
|
|
|
namespace g {
|
|
|
|
class gstr;
|
|
class gstrp;
|
|
|
|
// Replacement for raw char*
|
|
class gstrp : public gbuf<char>
|
|
{
|
|
public:
|
|
explicit gstrp(char* p = nullptr) : gbuf(p) {}
|
|
|
|
gstrp(gstrp&& s) : gbuf(std::move(s)) {}
|
|
gstrp& operator=(gstrp&& s) { static_cast<gbuf<char>&>(*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<typename ...Args>
|
|
struct is_valid_arg
|
|
{
|
|
static const bool value = true;
|
|
};
|
|
|
|
template<typename T, class... Args>
|
|
struct is_valid_arg<T, Args...>
|
|
{
|
|
static_assert(std::is_trivial<typename std::remove_reference<T>::type>::value, "An object passed to a printf-like function");
|
|
static const bool value =
|
|
std::is_trivial<typename std::remove_reference<T>::type>::value
|
|
&& is_valid_arg<Args...>::value;
|
|
};
|
|
|
|
template<typename T>
|
|
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<typename Func, typename ...Args>
|
|
static auto call (const Func& func, const char* format, Args&& ...args) -> decltype(func(dummy_format_string))
|
|
{
|
|
g_assert (printf_helper::is_valid_arg<decltype(printf_helper::transform_printf_arg (std::forward<Args> (args)))...>::value);
|
|
return func(format, printf_helper::transform_printf_arg (std::forward<Args> (args))...);
|
|
}
|
|
|
|
template<typename Func, typename ...Args>
|
|
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<decltype(printf_helper::transform_printf_arg (std::forward<Args> (args)))...>::value);
|
|
return call_helper (func, format, printf_helper::transform_printf_arg (std::forward<Args> (args))...);
|
|
}
|
|
|
|
template<typename Func, typename ...Args>
|
|
static void call_void (const Func& func, const char* format, Args&& ...args)
|
|
{
|
|
g_assert (printf_helper::is_valid_arg<decltype(printf_helper::transform_printf_arg (std::forward<Args> (args)))...>::value);
|
|
func (format, printf_helper::transform_printf_arg (std::forward<Args> (args))...);
|
|
}
|
|
|
|
template<typename Func, typename ...Args>
|
|
static void callv_void (const Func& func, const char* format, Args&& ...args)
|
|
{
|
|
g_assert (printf_helper::is_valid_arg<decltype(printf_helper::transform_printf_arg (std::forward<Args> (args)))...>::value);
|
|
call_helper_void (func, format, printf_helper::transform_printf_arg (std::forward<Args> (args))...);
|
|
}
|
|
|
|
private:
|
|
template<typename Func>
|
|
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<typename Func>
|
|
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<const char*>(*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<typename ...Args>
|
|
static gstr printf(const char* format, Args&& ...args)
|
|
{
|
|
return wrap_new (printf_helper::callv (g_strdup_vprintf, format, std::forward<Args> (args)...));
|
|
}
|
|
|
|
template<typename ...Args>
|
|
void set_printf (const char* format, Args&& ...args)
|
|
{
|
|
set_new (printf_helper::callv (g_strdup_vprintf, format, std::forward<Args> (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<typename T>
|
|
inline const T& printf_helper::transform_printf_arg (const T& arg)
|
|
{
|
|
g_assert (is_valid_arg<T>::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<gstr>;
|
|
using gstrset = std::unordered_set<gstr>;
|
|
using gstrmap = std::unordered_map<gstr, gstr>;
|
|
|
|
|
|
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<typename ...Args>
|
|
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> (args)...);
|
|
}
|
|
|
|
template<typename ...Args>
|
|
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>(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<typename ...Args>
|
|
inline void moo_g_print(const char* format, Args&& ...args)
|
|
{
|
|
static_assert(g::printf_helper::is_valid_arg<Args...>::value, "Passed an object to g_strdup_printf");
|
|
g_print (format, std::forward<Args> (args)...);
|
|
}
|
|
|
|
template<typename ...Args>
|
|
inline char* moo_g_strdup_printf (const char* format, Args&& ...args)
|
|
{
|
|
static_assert(g::printf_helper::is_valid_arg<Args...>::value, "Passed an object to g_strdup_printf");
|
|
return g_strdup_printf (format, std::forward<Args> (args)...);
|
|
}
|
|
|
|
#undef g_strdup_printf
|
|
#undef g_print
|
|
#define g_strdup_printf moo_g_strdup_printf
|
|
#define g_print moo_g_print
|