#ifndef UTILS_HPP #define UTILS_HPP #include #include #include #include #include #include #include #include #include #include #include "utils/common.hpp" #include "utils/error.hpp" #include "utils/optional.hpp" #include "utils/result.hpp" namespace cpptrace { namespace detail { bool isatty(int fd); int fileno(std::FILE* stream); inline std::vector split(const std::string& str, const std::string& delims) { std::vector vec; std::size_t old_pos = 0; std::size_t pos = 0; while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) { vec.emplace_back(str.substr(old_pos, pos - old_pos)); old_pos = pos + 1; } vec.emplace_back(str.substr(old_pos)); return vec; } template std::string join(const C& container, const std::string& delim) { auto iter = std::begin(container); auto end = std::end(container); std::string str; if(std::distance(iter, end) > 0) { str += *iter; while(++iter != end) { str += delim; str += *iter; } } return str; } // first value in a sorted range such that *it <= value template ForwardIt first_less_than_or_equal(ForwardIt begin, ForwardIt end, const T& value) { auto it = std::upper_bound(begin, end, value); // it is first > value, we want first <= value if(it != begin) { return --it; } return end; } // first value in a sorted range such that *it <= value template ForwardIt first_less_than_or_equal(ForwardIt begin, ForwardIt end, const T& value, Compare compare) { auto it = std::upper_bound(begin, end, value, compare); // it is first > value, we want first <= value if(it != begin) { return --it; } return end; } constexpr const char* const whitespace = " \t\n\r\f\v"; inline std::string trim(const std::string& str) { if(str.empty()) { return ""; } const std::size_t left = str.find_first_not_of(whitespace); const std::size_t right = str.find_last_not_of(whitespace) + 1; return str.substr(left, right - left); } inline bool starts_with(const std::string& str, const std::string& prefix) { return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0; } inline bool is_little_endian() { std::uint16_t num = 0x1; const auto* ptr = (std::uint8_t*)# return ptr[0] == 1; } // Modified from // https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c template struct byte_swapper; template struct byte_swapper { T operator()(T val) { return val; } }; template struct byte_swapper { T operator()(T val) { return (((val >> 8) & 0xff) | ((val & 0xff) << 8)); } }; template struct byte_swapper { T operator()(T val) { return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); } }; template struct byte_swapper { T operator()(T val) { return (((val & 0xff00000000000000ULL) >> 56) | ((val & 0x00ff000000000000ULL) >> 40) | ((val & 0x0000ff0000000000ULL) >> 24) | ((val & 0x000000ff00000000ULL) >> 8 ) | ((val & 0x00000000ff000000ULL) << 8 ) | ((val & 0x0000000000ff0000ULL) << 24) | ((val & 0x000000000000ff00ULL) << 40) | ((val & 0x00000000000000ffULL) << 56)); } }; template::value, int>::type = 0> T byteswap(T value) { return byte_swapper{}(value); } void enable_virtual_terminal_processing_if_needed() noexcept; constexpr unsigned n_digits(unsigned value) noexcept { return value < 10 ? 1 : 1 + n_digits(value / 10); } // TODO: Re-evaluate use of off_t template::value, int>::type = 0> Result load_bytes(std::FILE* object_file, off_t offset) { T object; if(std::fseek(object_file, offset, SEEK_SET) != 0) { return internal_error("fseek error"); } if(std::fread(&object, sizeof(T), 1, object_file) != 1) { return internal_error("fread error"); } return object; } // shamelessly stolen from stackoverflow bool directory_exists(const std::string& path); inline std::string basename(const std::string& path, bool maybe_windows = false) { // Assumes no trailing /'s auto pos = path.find_last_of(maybe_windows ? "/\\" : "/"); if(pos == std::string::npos) { return path; } else { return path.substr(pos + 1); } } // A way to cast to unsigned long long without "warning: useless cast to type" template unsigned long long to_ull(T t) { return static_cast(t); } template frame_ptr to_frame_ptr(T t) { return static_cast(t); } // A way to cast to U without "warning: useless cast to type" template U to(V v) { return static_cast(v); } template T exchange(T& obj, U&& value) { T old = std::move(obj); obj = std::forward(value); return old; } struct monostate {}; // TODO: Rework some stuff here. Not sure deleters should be optional or moved. // Also allow file_wrapper file = std::fopen(object_path.c_str(), "rb"); template< typename T, typename D, // Note: Previously checked if D was invocable and returned void but this kept causing problems for MSVC // == 19.38-specific msvc bug https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565 // <= 19.23 msvc also appears to fail (but for a different reason https://godbolt.org/z/6Y5EvdWPK) // <= 19.39 msvc also has trouble with it for different reasons https://godbolt.org/z/aPPPT7z3z typename std::enable_if< std::is_standard_layout::value && std::is_trivial::value, int >::type = 0, typename std::enable_if< std::is_nothrow_move_constructible::value, int >::type = 0 > class raii_wrapper { T obj; optional deleter; public: raii_wrapper(T obj, D deleter) : obj(obj), deleter(deleter) {} raii_wrapper(raii_wrapper&& other) noexcept : obj(std::move(other.obj)), deleter(std::move(other.deleter)) { other.deleter = nullopt; } raii_wrapper(const raii_wrapper&) = delete; raii_wrapper& operator=(raii_wrapper&&) = delete; raii_wrapper& operator=(const raii_wrapper&) = delete; ~raii_wrapper() { if(deleter.has_value()) { deleter.unwrap()(obj); } } operator T&() { return obj; } operator const T&() const { return obj; } T& get() { return obj; } const T& get() const { return obj; } }; template raii_wrapper::type, D> raii_wrap(T obj, D deleter) { return raii_wrapper::type, D>(obj, deleter); } inline void file_deleter(std::FILE* ptr) { if(ptr) { fclose(ptr); } } using file_wrapper = raii_wrapper; template auto make_unique(Args&&... args) -> typename std::enable_if::value, std::unique_ptr>::type { return std::unique_ptr(new T(std::forward(args)...)); } template class maybe_owned { std::unique_ptr owned; T* ptr; public: maybe_owned(T* ptr) : ptr(ptr) {} maybe_owned(std::unique_ptr&& owned) : owned(std::move(owned)), ptr(this->owned.get()) {} T* operator->() { return ptr; } T& operator*() { return *ptr; } }; // template // class scope_guard { // F f; // bool active; // public: // scope_guard(F&& f) : f(std::forward(f)), active(true) {} // ~scope_guard() { // if(active) { // f(); // } // } // scope_guard(const scope_guard&) = delete; // scope_guard(scope_guard&& other) : f(std::move(other.f)), active(exchange(other.active, false)) {} // scope_guard& operator=(const scope_guard&) = delete; // scope_guard& operator=(scope_guard&& other) { // f = std::move(other.f); // active = exchange(other.active, false); // return *this; // } // }; // template // auto scope_exit(F&& f) { // return scope_guard(std::forward(f)); // } } } #endif