source: XIOS3/trunk/extern/cpptrace/src/common.hpp @ 2573

Last change on this file since 2573 was 2573, checked in by ymipsl, 9 months ago

create new external source lib : cpptrace, for statck trace output
YM

File size: 12.6 KB
Line 
1#ifndef COMMON_HPP
2#define COMMON_HPP
3
4#ifdef _MSC_VER
5#define CPPTRACE_FORCE_NO_INLINE __declspec(noinline)
6#define CPPTRACE_PFUNC __FUNCSIG__
7#define CPPTRACE_MAYBE_UNUSED
8#pragma warning(push)
9#pragma warning(disable: 4505) // Unused local function
10#else
11#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
12#define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__
13#define CPPTRACE_MAYBE_UNUSED __attribute__((unused))
14#endif
15
16#include <cstdint>
17#include <cstdio>
18#include <cstdlib>
19#include <exception>
20#include <ios>
21#include <sstream>
22#include <string>
23#include <type_traits>
24#include <utility>
25#include <vector>
26#include <memory>
27#include <new>
28
29#define IS_WINDOWS 0
30#define IS_LINUX 0
31#define IS_APPLE 0
32
33#if defined(_WIN32)
34 #undef IS_WINDOWS
35 #define IS_WINDOWS 1
36#elif defined(__linux)
37 #undef IS_LINUX
38 #define IS_LINUX 1
39#elif defined(__APPLE__)
40 #undef IS_APPLE
41 #define IS_APPLE 1
42#else
43 #error "Unexpected platform"
44#endif
45
46#define IS_CLANG 0
47#define IS_GCC 0
48#define IS_MSVC 0
49
50#if defined(__clang__)
51 #undef IS_CLANG
52 #define IS_CLANG 1
53#elif defined(__GNUC__) || defined(__GNUG__)
54 #undef IS_GCC
55 #define IS_GCC 1
56#elif defined(_MSC_VER)
57 #undef IS_MSVC
58 #define IS_MSVC 1
59#else
60 #error "Unsupported compiler"
61#endif
62
63#if IS_WINDOWS
64 #include <windows.h>
65#else
66 #include <sys/stat.h>
67#endif
68
69// Lightweight std::source_location.
70struct source_location {
71    // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
72    const char* const file;
73    //const char* const function; // disabled for now due to static constexpr restrictions
74    // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
75    const int line;
76    constexpr source_location(
77        //const char* _function /*= __builtin_FUNCTION()*/,
78//        const char* _file     = __builtin_FILE(),
79//        int _line             = __builtin_LINE()
80        const char* _file     = __FILE__,
81        int _line             = __LINE__
82    ) : file(_file), /*function(_function),*/ line(_line) {}
83};
84
85CPPTRACE_MAYBE_UNUSED
86static void primitive_assert_impl(
87    bool condition,
88    bool verify,
89    const char* expression,
90    const char* signature,
91    source_location location,
92    const char* message = nullptr
93) {
94    if(!condition) {
95        const char* action = verify ? "verification" : "assertion";
96        const char* name   = verify ? "verify"       : "assert";
97        if(message == nullptr) {
98            (void) fprintf(
99                stderr,
100                "Cpptrace %s failed at %s:%d: %s\n",
101                action, location.file, location.line, signature
102            );
103        } else {
104            (void) fprintf(
105                stderr,
106                "Cpptrace %s failed at %s:%d: %s: %s\n",
107                action, location.file, location.line, signature, message
108            );
109        }
110        (void) fprintf(stderr, "    primitive_%s(%s);\n", name, expression);
111        std::abort();
112    }
113}
114
115template<typename T>
116void nothing() {}
117#define PHONY_USE(E) (nothing<decltype(E)>())
118
119// Still present in release mode, nonfatal
120#define internal_verify(c, ...) primitive_assert_impl(c, true, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__)
121
122#ifndef NDEBUG
123    #define CPPTRACE_PRIMITIVE_ASSERT(c, ...) \
124    primitive_assert_impl(c, false, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__)
125#else
126    #define CPPTRACE_PRIMITIVE_ASSERT(c, ...) PHONY_USE(c)
127#endif
128
129CPPTRACE_MAYBE_UNUSED
130static std::vector<std::string> split(const std::string& str, const std::string& delims) {
131    std::vector<std::string> vec;
132    size_t old_pos = 0;
133    size_t pos = 0;
134    while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) {
135        vec.emplace_back(str.substr(old_pos, pos - old_pos));
136        old_pos = pos + 1;
137    }
138    vec.emplace_back(str.substr(old_pos));
139    return vec;
140}
141
142template<typename C>
143CPPTRACE_MAYBE_UNUSED
144static std::string join(const C& container, const std::string& delim) {
145    auto iter = std::begin(container);
146    auto end = std::end(container);
147    std::string str;
148    if(std::distance(iter, end) > 0) {
149        str += *iter;
150        while(++iter != end) {
151            str += delim;
152            str += *iter;
153        }
154    }
155    return str;
156}
157
158constexpr const char* const whitespace = " \t\n\r\f\v";
159
160CPPTRACE_MAYBE_UNUSED
161static std::string trim(const std::string& str) {
162    if(str.empty()) {
163        return "";
164    }
165    const size_t left = str.find_first_not_of(whitespace);
166    const size_t right = str.find_last_not_of(whitespace) + 1;
167    return str.substr(left, right - left);
168}
169
170CPPTRACE_MAYBE_UNUSED
171static std::string to_hex(uintptr_t addr) {
172    std::stringstream sstream;
173    sstream<<std::hex<<addr;
174    return std::move(sstream).str();
175}
176
177CPPTRACE_MAYBE_UNUSED
178static bool is_little_endian() {
179    uint16_t num = 0x1;
180    auto* ptr = (uint8_t*)&num;
181    return ptr[0] == 1;
182}
183
184// Modified from
185// https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c
186template<typename T, size_t N>
187struct byte_swapper;
188
189template<typename T>
190struct byte_swapper<T, 1> {
191    T operator()(T val) {
192        return val;
193    }
194};
195
196template<typename T>
197struct byte_swapper<T, 2> {
198    T operator()(T val) {
199        return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
200    }
201};
202
203template<typename T>
204struct byte_swapper<T, 4> {
205    T operator()(T val) {
206        return ((((val) & 0xff000000) >> 24) |
207                (((val) & 0x00ff0000) >>  8) |
208                (((val) & 0x0000ff00) <<  8) |
209                (((val) & 0x000000ff) << 24));
210    }
211};
212
213template<typename T>
214struct byte_swapper<T, 8> {
215    T operator()(T val) {
216        return ((((val) & 0xff00000000000000ull) >> 56) |
217                (((val) & 0x00ff000000000000ull) >> 40) |
218                (((val) & 0x0000ff0000000000ull) >> 24) |
219                (((val) & 0x000000ff00000000ull) >> 8 ) |
220                (((val) & 0x00000000ff000000ull) << 8 ) |
221                (((val) & 0x0000000000ff0000ull) << 24) |
222                (((val) & 0x000000000000ff00ull) << 40) |
223                (((val) & 0x00000000000000ffull) << 56));
224    }
225};
226
227template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
228T byteswap(T value) {
229    return byte_swapper<T, sizeof(T)>{}(value);
230}
231
232CPPTRACE_MAYBE_UNUSED
233inline void enable_virtual_terminal_processing_if_needed() {
234    // enable colors / ansi processing if necessary
235    #if IS_WINDOWS
236     // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
237     #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
238      constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
239     #endif
240     HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
241     DWORD dwMode = 0;
242     if(hOut == INVALID_HANDLE_VALUE) return;
243     if(!GetConsoleMode(hOut, &dwMode)) return;
244     if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
245     if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return;
246    #endif
247}
248
249CPPTRACE_MAYBE_UNUSED
250// NOLINTNEXTLINE(misc-no-recursion)
251inline constexpr unsigned n_digits(unsigned value) {
252    return value < 10 ? 1 : 1 + n_digits(value / 10);
253}
254static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result");
255static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result");
256static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result");
257static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
258static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
259
260// TODO: Re-evaluate use of off_t
261template<typename T, typename std::enable_if<std::is_pod<T>::value, int>::type = 0>
262T load_bytes(FILE* obj_file, off_t offset) {
263    T object;
264    internal_verify(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
265    internal_verify(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
266    return object;
267}
268
269class file_error : std::exception {
270    const char* what() const noexcept override {
271        return "Unable to read file";
272    }
273};
274
275struct nullopt_t {};
276
277static constexpr nullopt_t nullopt;
278
279template<typename T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0>
280class optional {
281    bool holds_value = false;
282
283    union {
284        T uvalue;
285    };
286
287public:
288    // clang-tidy false positive
289    // NOLINTNEXTLINE(modernize-use-equals-default)
290    optional() noexcept {}
291
292    optional(nullopt_t) noexcept {}
293
294    ~optional() {
295        reset();
296    }
297
298    optional(const optional& other) : holds_value(other.holds_value) {
299        if(holds_value) {
300            new (static_cast<void*>(std::addressof(uvalue))) T(other.uvalue);
301        }
302    }
303
304    optional(optional&& other) noexcept(std::is_nothrow_move_constructible<T>::value) : holds_value(other.holds_value) {
305        if(holds_value) {
306            new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
307        }
308    }
309
310    optional& operator=(const optional& other) {
311        optional copy(other);
312        swap(*this, copy);
313        return *this;
314    }
315
316    optional& operator=(optional&& other) noexcept(
317        std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value
318    ) {
319        reset();
320        if(other.holds_value) {
321            new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
322            holds_value = true;
323        }
324        return *this;
325    }
326
327    template<
328        typename U = T,
329        typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
330    >
331    // clang-tidy false positive
332    // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
333    optional(U&& value) : holds_value(true) {
334        new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
335    }
336
337    template<
338        typename U = T,
339        typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
340    >
341    optional& operator=(U&& value) {
342        if(holds_value) {
343            uvalue = std::forward<U>(value);
344        } else {
345            new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
346            holds_value = true;
347        }
348        return *this;
349    }
350
351    optional& operator=(nullopt_t) noexcept {
352        reset();
353        return *this;
354    }
355
356    void swap(optional& other) {
357        if(holds_value && other.holds_value) {
358            std::swap(uvalue, other.uvalue);
359        } else if(holds_value && !other.holds_value) {
360            new (&other.uvalue) T(std::move(uvalue));
361            uvalue.~T();
362        } else if(!holds_value && other.holds_value) {
363            new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
364            other.uvalue.~T();
365        }
366        std::swap(holds_value, other.holds_value);
367    }
368
369    bool has_value() const {
370        return holds_value;
371    }
372
373    operator bool() const {
374        return holds_value;
375    }
376
377    void reset() {
378        if(holds_value) {
379            uvalue.~T();
380        }
381        holds_value = false;
382    }
383
384    T& unwrap() & {
385        if(!holds_value) {
386            throw std::runtime_error{"Optional does not contain a value"};
387        }
388        return uvalue;
389    }
390
391    const T& unwrap() const & {
392        if(!holds_value) {
393            throw std::runtime_error{"Optional does not contain a value"};
394        }
395        return uvalue;
396    }
397
398    T&& unwrap() && {
399        if(!holds_value) {
400            throw std::runtime_error{"Optional does not contain a value"};
401        }
402        return std::move(uvalue);
403    }
404
405    const T&& unwrap() const && {
406        if(!holds_value) {
407            throw std::runtime_error{"Optional does not contain a value"};
408        }
409        return std::move(uvalue);
410    }
411
412    template<typename U>
413    T value_or(U&& default_value) const & {
414        return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
415    }
416
417    template<typename U>
418    T value_or(U&& default_value) && {
419        return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
420    }
421};
422
423// shamelessly stolen from stackoverflow
424CPPTRACE_MAYBE_UNUSED
425static bool directory_exists(const std::string& path) {
426    #if IS_WINDOWS
427    DWORD dwAttrib = GetFileAttributesA(path.c_str());
428    return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
429    #else
430    struct stat sb;
431    return stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode);
432    #endif
433}
434
435CPPTRACE_MAYBE_UNUSED
436static std::string basename(const std::string& path) {
437    // Assumes no trailing /'s
438    auto pos = path.rfind('/');
439    if(pos == std::string::npos) {
440        return path;
441    } else {
442        return path.substr(pos + 1);
443    }
444}
445
446#ifdef _MSC_VER
447#pragma warning(pop)
448#endif
449
450#endif
Note: See TracBrowser for help on using the repository browser.