Zth (libzth)
Loading...
Searching...
No Matches
util.h
Go to the documentation of this file.
1#ifndef ZTH_UTIL_H
2#define ZTH_UTIL_H
3/*
4 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 */
8
18#include <libzth/macros.h>
19
20#include <libzth/config.h>
21
26#define ZTH_STRINGIFY_(x) #x
30#define ZTH_STRINGIFY(x) ZTH_STRINGIFY_(x)
31
38#ifndef likely
39# ifdef __GNUC__
40# define likely(expr) \
41 __builtin_expect(!!(expr) /* NOLINT(readability-simplify-boolean-expr) */, 1)
42# else
43# define likely(expr) (expr)
44# endif
45#endif
46
53#ifndef unlikely
54# ifdef __GNUC__
55# define unlikely(expr) \
56 __builtin_expect(!!(expr) /* NOLINT(readability-simplify-boolean-expr) */, 0)
57# else
58# define unlikely(expr) (expr)
59# endif
60#endif
61
62#include <assert.h>
63
65#define ZTH_GET_MACRO_ARGN( \
66 _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, NAME, ...) \
67 NAME
68
69#ifndef FOREACH
70# define FOREACH_0(WHAT)
71# define FOREACH_1(WHAT, X) WHAT(X)
72# define FOREACH_2(WHAT, X, ...) \
73 WHAT(X) FOREACH_1(WHAT, __VA_ARGS__)
74# define FOREACH_3(WHAT, X, ...) \
75 WHAT(X) FOREACH_2(WHAT, __VA_ARGS__)
76# define FOREACH_4(WHAT, X, ...) \
77 WHAT(X) FOREACH_3(WHAT, __VA_ARGS__)
78# define FOREACH_5(WHAT, X, ...) \
79 WHAT(X) FOREACH_4(WHAT, __VA_ARGS__)
80# define FOREACH_6(WHAT, X, ...) \
81 WHAT(X) FOREACH_5(WHAT, __VA_ARGS__)
82# define FOREACH_7(WHAT, X, ...) \
83 WHAT(X) FOREACH_6(WHAT, __VA_ARGS__)
84# define FOREACH_8(WHAT, X, ...) \
85 WHAT(X) FOREACH_7(WHAT, __VA_ARGS__)
86# define FOREACH_9(WHAT, X, ...) \
87 WHAT(X) FOREACH_8(WHAT, __VA_ARGS__)
88# define FOREACH_10(WHAT, X, ...) \
89 WHAT(X) FOREACH_9(WHAT, __VA_ARGS__)
90# define FOREACH_11(WHAT, X, ...) \
91 WHAT(X) FOREACH_10(WHAT, __VA_ARGS__)
92# define FOREACH_12(WHAT, X, ...) \
93 WHAT(X) FOREACH_11(WHAT, __VA_ARGS__)
94# define FOREACH_13(WHAT, X, ...) \
95 WHAT(X) FOREACH_12(WHAT, __VA_ARGS__)
96# define FOREACH_14(WHAT, X, ...) \
97 WHAT(X) FOREACH_13(WHAT, __VA_ARGS__)
98# define FOREACH_15(WHAT, X, ...) \
99 WHAT(X) FOREACH_14(WHAT, __VA_ARGS__)
100# define FOREACH_16(WHAT, X, ...) \
101 WHAT(X) FOREACH_15(WHAT, __VA_ARGS__)
102//... repeat as needed
103
107# define FOREACH(action, ...) \
108 ZTH_GET_MACRO_ARGN( \
109 0, ##__VA_ARGS__, FOREACH_16, FOREACH_15, FOREACH_14, FOREACH_13, FOREACH_12, \
110 FOREACH_11, FOREACH_10, FOREACH_9, FOREACH_8, FOREACH_7, FOREACH_6, FOREACH_5, \
111 FOREACH_4, FOREACH_3, FOREACH_2, FOREACH_1, FOREACH_0) \
112 (action, ##__VA_ARGS__)
113#endif
114
115#ifndef REVERSE
116# define REVERSE_0()
117# define REVERSE_1(a) a
118# define REVERSE_2(a, ...) REVERSE_1(__VA_ARGS__), a
119# define REVERSE_3(a, ...) REVERSE_2(__VA_ARGS__), a
120# define REVERSE_4(a, ...) REVERSE_3(__VA_ARGS__), a
121# define REVERSE_5(a, ...) REVERSE_4(__VA_ARGS__), a
122# define REVERSE_6(a, ...) REVERSE_5(__VA_ARGS__), a
123# define REVERSE_7(a, ...) REVERSE_6(__VA_ARGS__), a
124# define REVERSE_8(a, ...) REVERSE_7(__VA_ARGS__), a
125# define REVERSE_9(a, ...) REVERSE_8(__VA_ARGS__), a
126# define REVERSE_10(a, ...) REVERSE_9(__VA_ARGS__), a
127# define REVERSE_11(a, ...) REVERSE_10(__VA_ARGS__), a
128# define REVERSE_12(a, ...) REVERSE_11(__VA_ARGS__), a
129# define REVERSE_13(a, ...) REVERSE_12(__VA_ARGS__), a
130# define REVERSE_14(a, ...) REVERSE_13(__VA_ARGS__), a
131# define REVERSE_15(a, ...) REVERSE_14(__VA_ARGS__), a
132# define REVERSE_16(a, ...) REVERSE_15(__VA_ARGS__), a
133# define REVERSE(...) \
134 ZTH_GET_MACRO_ARGN( \
135 0, ##__VA_ARGS__, REVERSE_16, REVERSE_15, REVERSE_14, REVERSE_13, REVERSE_12, \
136 REVERSE_11, REVERSE_10, REVERSE_9, REVERSE_8, REVERSE_7, REVERSE_6, REVERSE_5, \
137 REVERSE_4, REVERSE_3, REVERSE_2, REVERSE_1, REVERSE_0) \
138 (__VA_ARGS__)
139#endif
140
141#include <stdarg.h>
142
143#ifdef __cplusplus
144# include <cstdio>
145# include <cstdlib>
146# include <cstring>
147# include <limits>
148# include <memory>
149# include <string>
150# include <vector>
151
152# if __cplusplus >= 201103L
153# include <cinttypes>
154# else
155# include <inttypes.h>
156# endif
157
158# ifdef ZTH_HAVE_PTHREAD
159# include <pthread.h>
160# endif
161
162# ifdef ZTH_OS_WINDOWS
163# include <process.h>
164# else
165# include <sys/types.h>
166# include <unistd.h>
167# endif
168
169EXTERN_C ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) void
170zth_logv(char const* fmt, va_list arg);
171
172# ifdef __cplusplus
177# define ZTH_DBG_PREFIX " > "
178
189# define zth_dbg(group, fmt, a...) \
190 do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \
191 if(::zth::Config::SupportDebugPrint && ::zth::Config::Print_##group > 0 \
192 && zth_config(EnableDebugPrint)) { \
193 if(::zth::Config::EnableColorLog) \
194 ::zth::log_color( \
195 ::zth::Config::Print_##group, \
196 ZTH_DBG_PREFIX "zth::" ZTH_STRINGIFY(group) ": " fmt \
197 "\n", \
198 ##a); \
199 else \
200 ::zth::log( \
201 ZTH_DBG_PREFIX "zth::" ZTH_STRINGIFY(group) ": " fmt \
202 "\n", \
203 ##a); \
204 } \
205 } while(0)
206
211# ifndef NDEBUG
212# define zth_assert(expr) \
213 do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \
214 if(unlikely(::zth::Config::EnableAssert && !(expr))) \
215 ::zth::assert_handler( \
216 __FILE__, __LINE__, \
217 ::zth::Config::EnableFullAssert ? ZTH_STRINGIFY(expr) \
218 : nullptr); \
219 } while(false)
220# else
221# define zth_assert(...) \
222 do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \
223 } while(0)
224# endif
225# endif
226
227# ifndef ZTH_CLASS_NOCOPY
228# if __cplusplus >= 201103L
229# define ZTH_CLASS_NOCOPY(Class) \
230 public: \
231 Class(Class const&) = delete; \
232 Class(Class&&) = \
233 delete; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) \
234 */ \
235 Class& operator=(Class const&) noexcept = delete; \
236 Class& operator=(Class&&) noexcept = \
237 delete; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) \
238 */ \
239 private:
240# else
241# define ZTH_CLASS_NOCOPY(Class) \
242 private: \
243 Class(Class const&); \
244 Class& operator=(Class const&);
245# endif
246# endif
247
248# include <libzth/zmq.h>
249
250namespace zth {
251
252ZTH_EXPORT char const* banner() noexcept;
253
254ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2), noreturn)) void
255abort(char const* fmt, ...) noexcept;
256
257ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0), noreturn)) void
258abortv(char const* fmt, va_list args) noexcept;
259
260ZTH_EXPORT __attribute__((noreturn)) void
261assert_handler(char const* file, int line, char const* expr);
262
263ZTH_EXPORT bool log_supports_ansi_colors() noexcept;
264
265ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 0))) void
266log_colorv(int color, char const* fmt, va_list args);
267
273ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) inline void
274log_color(int color, char const* fmt, ...)
275{
276 va_list args;
277 va_start(args, fmt);
278 log_colorv(color, fmt, args);
279 va_end(args);
280}
281
288ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) inline void
289logv(char const* fmt, va_list arg)
290{
291 ::zth_logv(fmt, arg);
292}
293
300ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline void log(char const* fmt, ...)
301{
302 va_list args;
303 va_start(args, fmt);
304 logv(fmt, args);
305 va_end(args);
306}
307
312typedef std::basic_string<char, std::char_traits<char>, Config::Allocator<char>::type> string;
313
322public:
323 cow_string()
324 : m_cstr()
325 {}
327 // cppcheck-suppress noExplicitConstructor
328 cow_string(char const* s)
329 : m_cstr(s)
330 {}
332 // cppcheck-suppress noExplicitConstructor
333 cow_string(string const& s)
334 : m_cstr()
335 , m_str(s)
336 {}
337
338 cow_string(cow_string const& s)
339 {
340 *this = s;
342
344 {
345 m_cstr = s.m_cstr;
346 m_str = s.m_str;
347 return *this;
349
350 cow_string& operator=(char const* s)
351 {
352 m_cstr = s;
353 m_str.clear();
354 return *this;
356
357 cow_string& operator=(string const& s)
358 {
359 m_cstr = nullptr;
360 m_str = s;
361 return *this;
362 }
364# if __cplusplus >= 201103L
365 cow_string(cow_string&& s) = default;
366 cow_string& operator=(cow_string&& s) = default;
368 // cppcheck-suppress noExplicitConstructor
369 cow_string(string&& s)
370 : m_cstr()
371 , m_str(std::move(s))
372 {}
373
374 cow_string& operator=(string&& s)
375 {
376 m_cstr = nullptr;
377 m_str = std::move(s);
378 return *this;
380
381 string str() &&
382 {
383 // cppcheck-suppress returnStdMoveLocal
384 return std::move(local());
385 }
386# endif
387
388 string const& str() const LREF_QUALIFIED
389 {
390 return local();
392
393 char const* c_str() const
394 {
395 return m_cstr ? m_cstr : m_str.c_str();
397
398 operator string const&() const
399 {
400 return str();
402
403 char const& at(size_t pos) const
404 {
405 return str().at(pos);
407
408 char operator[](size_t pos) const
409 {
410 return m_cstr ? m_cstr[pos] : str()[pos];
412
413 char& operator[](size_t pos)
414 {
415 return local()[pos];
417
418 char const* data() const
419 {
420 return c_str();
422
423 bool empty() const noexcept
424 {
425 return m_cstr ? *m_cstr == 0 : m_str.empty();
427
428 size_t size() const noexcept
429 {
430 return str().size();
432
433 size_t length() const noexcept
434 {
435 return str().length();
437
438 void clear() noexcept
439 {
440 m_cstr = nullptr;
441 m_str.clear();
443
444 bool isConst() const noexcept
445 {
446 return m_cstr != nullptr;
448
449 bool isLocal() const noexcept
450 {
451 return m_cstr == nullptr;
452 }
454protected:
455 string const& local() const
456 {
457 if(unlikely(m_cstr)) {
458 m_str = m_cstr;
459 m_cstr = nullptr;
460 }
461 return m_str;
463
464 string& local()
465 {
466 const_cast<cow_string const*>(this)->local();
467 return m_str;
468 }
469
470private:
471 mutable char const* m_cstr;
472 mutable string m_str;
473};
474
475__attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) string formatv(char const* fmt, va_list args);
476
480__attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline string format(char const* fmt, ...)
481{
482 va_list args;
483 va_start(args, fmt);
484 string res = formatv(fmt, args);
485 va_end(args);
486 return res;
487}
488
493template <typename T>
494inline cow_string str(T value)
495{
496 return cow_string(value);
497}
499template <>
500inline cow_string str<char>(char value)
501{
502 return format("%c", value);
503}
505template <>
506inline cow_string str<signed char>(signed char value)
507{
508 if(Config::UseLimitedFormatSpecifiers)
509 return format("%d", (int)value);
510 else
511 return format("%hhd", value);
512}
514template <>
515inline cow_string str<unsigned char>(unsigned char value)
516{
517 if(Config::UseLimitedFormatSpecifiers)
518 return format("%u", (unsigned int)value);
519 else
520 return format("%hhu", value);
521}
523template <>
524inline cow_string str<short>(short value)
525{
526 if(Config::UseLimitedFormatSpecifiers)
527 return format("%d", (int)value);
528 else
529 return format("%hd", value);
530}
532template <>
533inline cow_string str<unsigned short>(unsigned short value)
534{
535 if(Config::UseLimitedFormatSpecifiers)
536 return format("%u", (unsigned int)value);
537 else
538 return format("%hu", value);
539}
541template <>
542inline cow_string str<int>(int value)
543{
544 return format("%d", value);
545}
547template <>
548inline cow_string str<unsigned int>(unsigned int value)
549{
550 return format("%u", value);
551}
552
553namespace impl {
554ZTH_EXPORT ZTH_INLINE cow_string strull(unsigned long long value)
555{
557 if(value > (unsigned long long)std::numeric_limits<int>::max())
558 return format(
559 "0x%x%08x", (unsigned)(value >> 32U),
560 (unsigned)(value & 0xFFFFFFFFU));
561 else
562 return format("%d", (int)value);
563 } else {
564 return format("%llu", value);
565 }
566}
567} // namespace impl
569template <>
570inline cow_string str<long>(long value)
571{
572 if(Config::UseLimitedFormatSpecifiers) {
573 if(sizeof(long) == sizeof(int)
574 || (value <= std::numeric_limits<int>::max()
575 && value >= std::numeric_limits<int>::min()))
576 return format("%d", (int)value);
577 else
578 return impl::strull((unsigned long long)value);
579 } else {
580 return format("%ld", value);
581 }
582}
584template <>
585inline cow_string str<unsigned long>(unsigned long value)
586{
587 if(Config::UseLimitedFormatSpecifiers) {
588 if(sizeof(unsigned long) == sizeof(unsigned int)
589 || value <= std::numeric_limits<unsigned int>::max())
590 return format("%u", (unsigned int)value);
591 else
592 return impl::strull((unsigned long long)value);
593 } else {
594 return format("%lu", value);
595 }
596}
598template <>
599inline cow_string str<long long>(long long value)
600{
601 if(Config::UseLimitedFormatSpecifiers) {
602 if(value > std::numeric_limits<int>::max()
603 || value < std::numeric_limits<int>::min())
604 return impl::strull((unsigned long long)value);
605 else
606 return format("%d", (int)value);
607 } else {
608 return format("%lld", value);
609 }
610}
612template <>
613inline cow_string str<unsigned long long>(unsigned long long value)
614{
615 return impl::strull(value);
616}
617
618namespace impl {
619ZTH_EXPORT ZTH_INLINE cow_string strf(float value)
620{
622 if(!(value < (float)std::numeric_limits<int>::max()
623 && value > (float)-std::numeric_limits<int>::max()))
624 return "?range?";
625
626 int i = (int)value;
627 float fi = (float)i;
628 if(std::abs(fi - value) < 0.0005F)
629 return format("%d", i);
630
631 int f = std::min(std::abs((int)((value - fi) * 1000.0F + 0.5F)), 999);
632 return format("%d.%03d", i, f);
633 } else {
634 return format("%g", (double)value);
635 }
636}
637} // namespace impl
639template <>
640inline cow_string str<float>(float value)
641{
642 if(Config::UseLimitedFormatSpecifiers)
643 return impl::strf(value);
644 else
645 return format("%g", (double)value);
646}
648template <>
649inline cow_string str<double>(double value)
650{
651 if(Config::UseLimitedFormatSpecifiers)
652 return impl::strf((float)value);
653 else
654 return format("%g", value);
655}
657template <>
658inline cow_string str<long double>(long double value)
659{
660 if(Config::UseLimitedFormatSpecifiers)
661 return impl::strf((float)value);
662 else
663 return format("%Lg", value);
664}
665
666# if __cplusplus >= 201103L
667template <>
668inline cow_string str<string&&>(string&& value)
669{
670 return cow_string(std::move(value));
671}
672# endif
673
677inline string err(int e)
678{
679# ifdef ZTH_OS_BAREMETAL
680 // You are probably low on memory. Don't include all strerror strings in the binary.
681 return format("error %d", e);
682# elif defined(ZTH_HAVE_LIBZMQ)
683 return format("%s (error %d)", zmq_strerror(e), e);
684# elif ZTH_THREADS && !defined(ZTH_OS_WINDOWS)
685 char buf[128];
686# if !defined(ZTH_OS_LINUX) || (_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)
687 // XSI-compatible
688 return format("%s (error %d)", strerror_r(e, buf, sizeof(buf)) ? "Unknown error" : buf, e);
689# else
690 // GNU-specific
691 return format("%s (error %d)", strerror_r(e, buf, sizeof(buf)), e);
692# endif
693# else
694 // Not thread-safe
695 return format("%s (error %d)", strerror(e), e);
696# endif
698
700protected:
701 virtual char const* id_str() const = 0;
702 virtual ~UniqueIDBase() is_default
703 friend cow_string str<UniqueIDBase const&>(UniqueIDBase const&);
704};
706template <>
707inline cow_string str<UniqueIDBase const&>(UniqueIDBase const& value)
708{
709 return cow_string(value.id_str());
710}
711
716template <typename T, bool ThreadSafe = Config::EnableThreads>
717class UniqueID : public UniqueIDBase {
718# if __cplusplus < 201103L
719 ZTH_CLASS_NOCOPY(UniqueID)
720# else
721public:
722 UniqueID(UniqueID const&) = delete;
723 UniqueID& operator=(UniqueID const&) = delete;
724
725 UniqueID(UniqueID&& u) noexcept
726 : m_id{}
727 {
728 *this = std::move(u);
730
731 UniqueID& operator=(UniqueID&& u) noexcept
732 {
733 m_id = u.m_id;
734 m_name = std::move(u.m_name);
735 m_id_str = std::move(u.m_id_str);
736
737 u.m_id = 0;
738
739 return *this;
740 }
741# endif
742public:
743 static uint64_t getID() noexcept
744 {
745 return ThreadSafe ?
746# if GCC_VERSION < 40802L
747 __sync_add_and_fetch(&m_nextId, 1)
748# else
749 __atomic_add_fetch(&m_nextId, 1, __ATOMIC_RELAXED)
750# endif
751 : ++m_nextId;
753
754 explicit UniqueID(string const& name)
755 : m_id(getID())
756 , m_name(name)
757 {}
759# if __cplusplus >= 201103L
760 explicit UniqueID(string&& name)
761 : m_id(getID())
762 , m_name(std::move(name))
763 {}
764# endif
765
766 explicit UniqueID(char const* name = nullptr)
767 : m_id(getID())
768 {
769 if(name)
770 m_name = name;
772
773 virtual ~UniqueID() override is_default
774
775 void const* normptr() const noexcept
776 {
777 return this;
779
780 __attribute__((pure)) uint64_t id() const noexcept
781 {
782 return m_id;
784
785 string const& name() const noexcept
786 {
787 return m_name;
789
790 void setName(string const& name)
791 {
792 setName(name.c_str());
794
795 void setName(char const* name)
796 {
797 m_name = name;
798 m_id_str.clear();
799 changedName(this->name());
800 }
802# if __cplusplus >= 201103L
803 void setName(string&& name)
804 {
805 m_name = std::move(name);
806 m_id_str.clear();
807 changedName(this->name());
808 }
809# endif
810
811 virtual char const* id_str() const override
812 {
813 if(unlikely(m_id_str.empty())) {
814 m_id_str =
815# ifdef ZTH_OS_BAREMETAL
816 // No OS, no pid. And if newlib is used, don't try to format 64-bit
817 // ints.
818 format("%s #%u", name().empty() ? "Object" : name().c_str(),
819 (unsigned int)id());
820# else
821 format("%s #%u:%" PRIu64,
822 name().empty() ? "Object" : name().c_str(),
823# ifdef ZTH_OS_WINDOWS
824 (unsigned int)_getpid(),
825# else
826 (unsigned int)getpid(),
827# endif
828 id());
829# endif
830 if(unlikely(m_id_str.empty()))
831 // Should not happen, but make sure the string is not empty.
832 m_id_str = "?";
833 }
834
835 return m_id_str.c_str();
836 }
837
838private:
839 virtual void changedName(string const& UNUSED_PAR(name)) {}
840
841private:
842 uint64_t m_id;
843 string m_name;
844 string mutable m_id_str;
845 // If allocating once every ns, it takes more than 500 years until we run out of
846 // identifiers.
847 static uint64_t m_nextId;
848};
849
850template <typename T, bool ThreadSafe>
851uint64_t UniqueID<T, ThreadSafe>::m_nextId = 0;
853template <typename T, typename WhenTIsVoid>
854struct choose_type {
855 typedef T type;
856};
858template <typename WhenTIsVoid>
859struct choose_type<void, WhenTIsVoid> {
860 typedef WhenTIsVoid type;
861};
862
863# if __cplusplus >= 201103L
864template <size_t...>
865struct Sequence {};
866# ifndef DOXYGEN
867template <size_t N, size_t... S>
868struct SequenceGenerator : SequenceGenerator<N - 1, N - 1, S...> {};
869
870template <size_t... S>
871struct SequenceGenerator<0, S...> {
872 typedef Sequence<S...> type;
873};
874# endif
875# endif
879# ifdef _DEBUG
880template <typename T>
881class safe_ptr {
882public:
883 typedef safe_ptr type;
884 typedef T pointer_type;
886 // cppcheck-suppress noExplicitConstructor
887 constexpr safe_ptr(pointer_type* p) noexcept
888 : m_p(p)
889 {}
890
891 constexpr operator pointer_type*() const noexcept
892 {
893 return ptr();
895
896 constexpr operator bool() const noexcept
897 {
898 return ptr();
900
901 constexpr14 pointer_type* operator->() const noexcept
902 {
903 zth_assert(ptr());
904 return ptr();
906
907 constexpr14 pointer_type& operator*() const noexcept
908 {
909 zth_assert(ptr());
910 // cppcheck-suppress nullPointerRedundantCheck
911 return *ptr();
912 }
914protected:
915 constexpr pointer_type* ptr() const noexcept
916 {
917 return m_p;
918 }
919
920private:
921 pointer_type* m_p;
922};
923# else // _DEBUG
924
925// No checking, use use the pointer.
926template <typename T>
927class safe_ptr {
928public:
929 typedef T* type;
930};
931# endif // !_DEBUG
932
955template <typename T>
956class Singleton {
957public:
959 typedef T singleton_type;
960
961protected:
966 constexpr14 Singleton() noexcept
967 {
968 // Do not enforce construction of only one Singleton, only register the first one
969 // as 'the' instance.
970
971 if(!m_instance)
972 m_instance = static_cast<singleton_type*>(this);
973 }
974
979 ~Singleton()
980 {
981 if(m_instance == static_cast<singleton_type*>(this))
982 m_instance = nullptr;
983 }
984
985public:
990 __attribute__((pure)) constexpr14 static typename safe_ptr<singleton_type>::type
991 instance() noexcept
992 {
993 return m_instance;
994 }
995
996private:
998 static singleton_type* m_instance;
999};
1000
1001template <typename T>
1002typename Singleton<T>::singleton_type* Singleton<T>::m_instance = nullptr;
1003
1013template <typename T>
1014class ThreadLocalSingleton {
1015public:
1017 typedef T singleton_type;
1018
1019protected:
1025 __attribute__((no_sanitize_undefined)) ThreadLocalSingleton()
1026 {
1027 // Do not enforce construction of only one Singleton, only register the first one
1028 // as 'the' instance.
1029
1030 if(!ZTH_TLS_GET(m_instance))
1031 ZTH_TLS_SET(m_instance, static_cast<singleton_type*>(this));
1032 }
1033
1038 __attribute__((no_sanitize_undefined)) ~ThreadLocalSingleton()
1039 {
1040 if(ZTH_TLS_GET(m_instance) == static_cast<singleton_type*>(this))
1041 ZTH_TLS_SET(m_instance, nullptr);
1042 }
1043
1044public:
1049 __attribute__((pure)) static typename safe_ptr<singleton_type>::type instance() noexcept
1050 {
1051 return ZTH_TLS_GET(m_instance);
1052 }
1053
1054private:
1056 ZTH_TLS_MEMBER(singleton_type*, m_instance)
1057};
1058
1059template <typename T>
1061 typename ThreadLocalSingleton<T>::singleton_type*, ThreadLocalSingleton<T>::m_instance,
1062 nullptr)
1063
1064
1080template <typename T, int8_t Prealloc = 1, typename Allocator = typename Config::Allocator<T>::type>
1082public:
1083 typedef T value_type;
1084 typedef Allocator allocator_type;
1085 typedef std::vector<value_type, allocator_type> vector_type;
1086
1087 enum {
1088 prealloc_request = Prealloc > 0 ? Prealloc : 0,
1089 prealloc_request_size = prealloc_request * sizeof(value_type),
1090 vector_size = sizeof(vector_type),
1091 buffer_size =
1092 prealloc_request_size > vector_size ? prealloc_request_size : vector_size,
1093 prealloc = buffer_size / sizeof(value_type),
1094 };
1095
1099 // cppcheck-suppress uninitMemberVar
1100 constexpr small_vector() noexcept
1101 : m_size()
1102 {
1103 static_assert(prealloc < std::numeric_limits<uint8_t>::max(), "");
1104 }
1105
1109 ~small_vector()
1110 {
1111 if(is_small())
1112 resize(0);
1113 else
1114 vector().~vector_type();
1115 }
1116
1120 value_type& operator[](size_t index) noexcept
1121 {
1122 zth_assert(index < size());
1123 return data()[index];
1124 }
1125
1129 value_type const& operator[](size_t index) const noexcept
1130 {
1131 zth_assert(index < size());
1132 return data()[index];
1133 }
1134
1140 value_type& front() noexcept
1141 {
1142 return (*this)[0];
1143 }
1144
1150 value_type const& front() const noexcept
1151 {
1152 return (*this)[0];
1153 }
1154
1160 value_type& back() noexcept
1161 {
1162 zth_assert(!empty());
1163 return (*this)[size() - 1U];
1164 }
1165
1171 value_type const& back() const noexcept
1172 {
1173 zth_assert(!empty());
1174 return (*this)[size() - 1U];
1175 }
1176
1180 value_type* data() noexcept
1181 {
1182 return is_small() ? array() : vector().data();
1183 }
1184
1188 value_type const* data() const noexcept
1189 {
1190 return is_small() ? array() : vector().data();
1191 }
1192
1196 bool empty() const noexcept
1197 {
1198 return is_small() ? m_size == 0 : vector().empty();
1199 }
1200
1204 size_t size() const noexcept
1205 {
1206 return is_small() ? m_size : vector().size();
1207 }
1208
1214 void reserve(size_t new_cap)
1215 {
1216 if(is_small() && new_cap <= prealloc)
1217 return;
1218
1219 if(is_small())
1220 make_vector(new_cap);
1221
1222 vector().reserve(new_cap);
1223 }
1224
1229 size_t capacity() const noexcept
1230 {
1231 return is_small() ? (size_t)prealloc : vector().capacity();
1232 }
1233
1239 void clear() noexcept
1240 {
1241 if(is_small())
1242 resize(0);
1243 else
1244 vector().clear();
1245 }
1246
1250 void clear_and_release() noexcept
1251 {
1252 if(is_small()) {
1253 resize(0);
1254 } else {
1255 vector_type v;
1256 v.swap(vector());
1257 }
1258 }
1259
1264 void push_back(value_type const& v)
1265 {
1266 reserve(size() + 1U);
1267
1268 if(m_size < prealloc) {
1269 new(&array()[m_size]) value_type(v);
1270 m_size++;
1271 } else
1272 vector().push_back(v);
1273 }
1274
1275# if __cplusplus >= 201103L
1280 template <class... Args>
1281 void emplace_back(Args&&... args)
1282 {
1283 reserve(size() + 1U);
1284
1285 if(m_size < prealloc) {
1286 new(&array()[m_size]) value_type(std::forward<Args>(args)...);
1287 m_size++;
1288 } else
1289 vector().emplace_back(std::forward<Args>(args)...);
1290 }
1291# endif
1292
1298 void pop_back() noexcept
1299 {
1300 zth_assert(!empty());
1301 if(is_small())
1302 array()[--m_size].~value_type();
1303 else
1304 vector().pop_back();
1305 }
1306
1312 void resize(size_t count, value_type const& x = value_type())
1313 {
1314 if(is_small()) {
1315 while(count < m_size)
1316 array()[--m_size].~value_type();
1317
1318 if(count <= prealloc) {
1319 while(count > m_size) {
1320 new(&array()[m_size]) value_type(x);
1321 m_size++;
1322 }
1323 } else {
1324 make_vector(count);
1325 vector().resize(count, x);
1326 }
1327 } else
1328 vector().resize(count, x);
1329 }
1330
1331protected:
1335 bool is_small() const noexcept
1336 {
1337 return m_size <= prealloc;
1338 }
1339
1343 value_type* array() noexcept
1344 {
1345 zth_assert(is_small());
1346 return reinterpret_cast<value_type*>(m_buffer);
1347 }
1348
1352 value_type const* array() const noexcept
1353 {
1354 zth_assert(is_small());
1355 return reinterpret_cast<value_type const*>(m_buffer);
1356 }
1357
1361 vector_type& vector() noexcept
1362 {
1363 zth_assert(!is_small());
1364 void* buffer = m_buffer;
1365 return *reinterpret_cast<vector_type*>(buffer);
1366 }
1367
1371 vector_type const& vector() const noexcept
1372 {
1373 zth_assert(!is_small());
1374 void const* buffer = m_buffer;
1375 return *reinterpret_cast<vector_type const*>(buffer);
1376 }
1377
1381 void make_vector(size_t new_cap)
1382 {
1383 if(!is_small())
1384 return;
1385
1386 vector_type v;
1387 v.reserve(std::max(new_cap, (size_t)m_size));
1388
1389 for(size_t i = 0; i < m_size; i++) {
1390# if __cplusplus >= 201103L
1391 v.emplace_back(std::move(array()[i]));
1392# else
1393 v.push_back(array()[i]);
1394# endif
1395 array()[i].~value_type();
1396 }
1397
1398 m_size = prealloc + 1U;
1399 new(m_buffer) vector_type;
1400 vector().swap(v);
1401 }
1402
1403private:
1408 alignas(vector_type) alignas(value_type) char m_buffer[buffer_size];
1409
1417 uint8_t m_size;
1418};
1420template <size_t size>
1421struct smallest_uint_size {};
1422
1423template <
1424 uint64_t x, typename size = smallest_uint_size<
1425 x >= 0x100000000ULL ? 8U
1426 : x >= 0x10000U ? 4U
1427 : x >= 0x100U ? 2U
1428 : 1U> >
1429struct smallest_uint {
1430 typedef uint64_t type;
1431};
1433template <uint64_t x>
1434struct smallest_uint<x, smallest_uint_size<1> > {
1435 typedef uint8_t type;
1436};
1438template <uint64_t x>
1439struct smallest_uint<x, smallest_uint_size<2> > {
1440 typedef uint16_t type;
1441};
1443template <uint64_t x>
1444struct smallest_uint<x, smallest_uint_size<4> > {
1445 typedef uint32_t type;
1446};
1447
1448} // namespace zth
1449
1450#endif // __cplusplus
1451
1457#ifdef __cplusplus
1458EXTERN_C ZTH_EXPORT ZTH_INLINE void zth_banner()
1459{
1460 zth::banner();
1461}
1462#else
1463ZTH_EXPORT void zth_banner();
1464#endif
1465
1471ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2), noreturn)) void
1472zth_abort(char const* fmt, ...);
1473
1479#ifdef __cplusplus
1480EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) void
1481zth_log_color(int color, char const* fmt, ...)
1482{
1483 va_list args;
1484 va_start(args, fmt);
1485 zth::log_colorv(color, fmt, args);
1486 va_end(args);
1487}
1488#else
1489ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) void
1490zth_log_color(int color, char const* fmt, ...);
1491#endif
1492
1498#ifdef __cplusplus
1499EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void
1500zth_log(char const* fmt, ...)
1501{
1502 va_list args;
1503 va_start(args, fmt);
1504 zth::logv(fmt, args);
1505 va_end(args);
1506}
1507#else
1508ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void zth_log(char const* fmt, ...);
1509#endif
1510
1511#ifdef ZTH_OS_BAREMETAL
1512// newlib probably doesn't have these. Provide some default implementation for
1513// them.
1514EXTERN_C __attribute__((format(gnu_printf, 2, 0))) int asprintf(char** strp, const char* fmt, ...);
1515EXTERN_C __attribute__((format(gnu_printf, 2, 0))) int
1516vasprintf(char** strp, const char* fmt, va_list ap);
1517#endif
1518
1519#endif // ZTH_UTIL_H
Singleton pattern.
Definition util.h:954
T singleton_type
Alias of the T template parameter.
Definition util.h:957
Singleton pattern, but only per-thread.
Definition util.h:1012
T singleton_type
Alias of the T template parameter.
Definition util.h:1015
virtual char const * id_str() const =0
friend cow_string str(UniqueIDBase const &)
virtual ~UniqueIDBase()=default
Keeps track of a process-wide unique ID within the type T.
Definition util.h:715
Copy-on-write string.
Definition util.h:319
char operator[](size_t pos) const
Definition util.h:406
string str() &&
Definition util.h:379
bool isConst() const noexcept
Definition util.h:442
string const & local() const
Definition util.h:453
char const & at(size_t pos) const
Definition util.h:401
char const * c_str() const
Definition util.h:391
size_t length() const noexcept
Definition util.h:431
cow_string & operator=(cow_string const &s)
Definition util.h:341
size_t size() const noexcept
Definition util.h:426
char const * data() const
Definition util.h:416
void clear() noexcept
Definition util.h:436
bool empty() const noexcept
Definition util.h:421
bool isLocal() const noexcept
Definition util.h:447
Wrapper for a pointer, which checks validity of the pointer upon dereference.
Definition util.h:879
T pointer_type
Definition util.h:882
Change the name of a fiber returned by zth_async.
Definition async.h:173
A simple std::vector, which can contain Prealloc without heap allocation.
Definition util.h:1079
Allocator allocator_type
Definition util.h:1082
std::vector< value_type, allocator_type > vector_type
Definition util.h:1083
void zth_banner()
Prints a banner line with version and configuration information.
Definition util.h:1456
void zth_log(char const *fmt,...)
Logs a given printf()-like formatted string.
Definition util.h:1498
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:334
void zth_log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
Definition util.h:1479
void zth_logv(char const *fmt, va_list arg)
Prints the given printf()-like formatted string to stdout.
Definition zth_logv.cpp:17
void log_colorv(int color, char const *fmt, va_list args)
Logs a given printf()-like formatted string using an ANSI color code.
Definition util.cpp:233
char const * banner() noexcept
Prints a banner line with version and configuration information.
Definition util.cpp:37
void logv(char const *fmt, va_list arg)
Logs a given printf()-like formatted string.
Definition util.h:287
#define constexpr14
Definition macros.h:208
#define ZTH_TLS_DEFINE(type, var, init)
Definition macros.h:100
#define ZTH_TLS_GET(var)
Definition macros.h:104
#define is_default
Definition macros.h:212
#define ZTH_TLS_MEMBER(type, var)
Definition macros.h:102
#define ZTH_TLS_SET(var, value)
Definition macros.h:103
#define LREF_QUALIFIED
Definition macros.h:215
#define ZTH_ATTR_PRINTF
Definition macros.h:63
#define ZTH_INLINE
Definition macros.h:129
#define UNUSED_PAR(name)
Definition macros.h:78
STL namespace.
cow_string str(T value)
Returns an zth::string representation of the given value.
Definition util.h:492
string formatv(char const *fmt, va_list args)
Format like vsprintf(), but save the result in an zth::string.
Definition util.cpp:251
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:478
static bool const UseLimitedFormatSpecifiers
Use limited formatting specifiers.
Definition config.h:195
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:212
#define ZTH_CLASS_NOCOPY(Class)
Definition util.h:229
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:55