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
22#if defined(__cplusplus) && __cplusplus >= 201703L
23# include <tuple>
24# include <type_traits>
25#endif
26
31#define ZTH_STRINGIFY_(x) #x
35#define ZTH_STRINGIFY(x) ZTH_STRINGIFY_(x)
36
43#ifndef likely
44# ifdef __GNUC__
45# define likely(expr) \
46 __builtin_expect(!!(expr) /* NOLINT(readability-simplify-boolean-expr) */, 1)
47# else
48# define likely(expr) (expr)
49# endif
50#endif
51
58#ifndef unlikely
59# ifdef __GNUC__
60# define unlikely(expr) \
61 __builtin_expect(!!(expr) /* NOLINT(readability-simplify-boolean-expr) */, 0)
62# else
63# define unlikely(expr) (expr)
64# endif
65#endif
66
67#include <assert.h>
68
70#define ZTH_GET_MACRO_ARGN( \
71 _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, NAME, ...) \
72 NAME
73
74#ifndef FOREACH
75# define FOREACH_0(WHAT)
76# define FOREACH_1(WHAT, X) WHAT(X)
77# define FOREACH_2(WHAT, X, ...) \
78 WHAT(X) FOREACH_1(WHAT, __VA_ARGS__)
79# define FOREACH_3(WHAT, X, ...) \
80 WHAT(X) FOREACH_2(WHAT, __VA_ARGS__)
81# define FOREACH_4(WHAT, X, ...) \
82 WHAT(X) FOREACH_3(WHAT, __VA_ARGS__)
83# define FOREACH_5(WHAT, X, ...) \
84 WHAT(X) FOREACH_4(WHAT, __VA_ARGS__)
85# define FOREACH_6(WHAT, X, ...) \
86 WHAT(X) FOREACH_5(WHAT, __VA_ARGS__)
87# define FOREACH_7(WHAT, X, ...) \
88 WHAT(X) FOREACH_6(WHAT, __VA_ARGS__)
89# define FOREACH_8(WHAT, X, ...) \
90 WHAT(X) FOREACH_7(WHAT, __VA_ARGS__)
91# define FOREACH_9(WHAT, X, ...) \
92 WHAT(X) FOREACH_8(WHAT, __VA_ARGS__)
93# define FOREACH_10(WHAT, X, ...) \
94 WHAT(X) FOREACH_9(WHAT, __VA_ARGS__)
95# define FOREACH_11(WHAT, X, ...) \
96 WHAT(X) FOREACH_10(WHAT, __VA_ARGS__)
97# define FOREACH_12(WHAT, X, ...) \
98 WHAT(X) FOREACH_11(WHAT, __VA_ARGS__)
99# define FOREACH_13(WHAT, X, ...) \
100 WHAT(X) FOREACH_12(WHAT, __VA_ARGS__)
101# define FOREACH_14(WHAT, X, ...) \
102 WHAT(X) FOREACH_13(WHAT, __VA_ARGS__)
103# define FOREACH_15(WHAT, X, ...) \
104 WHAT(X) FOREACH_14(WHAT, __VA_ARGS__)
105# define FOREACH_16(WHAT, X, ...) \
106 WHAT(X) FOREACH_15(WHAT, __VA_ARGS__)
107//... repeat as needed
108
112# define FOREACH(action, ...) \
113 ZTH_GET_MACRO_ARGN( \
114 0, ##__VA_ARGS__, FOREACH_16, FOREACH_15, FOREACH_14, FOREACH_13, FOREACH_12, \
115 FOREACH_11, FOREACH_10, FOREACH_9, FOREACH_8, FOREACH_7, FOREACH_6, FOREACH_5, \
116 FOREACH_4, FOREACH_3, FOREACH_2, FOREACH_1, FOREACH_0) \
117 (action, ##__VA_ARGS__)
118#endif
119
120#ifndef REVERSE
121# define REVERSE_0()
122# define REVERSE_1(a) a
123# define REVERSE_2(a, ...) REVERSE_1(__VA_ARGS__), a
124# define REVERSE_3(a, ...) REVERSE_2(__VA_ARGS__), a
125# define REVERSE_4(a, ...) REVERSE_3(__VA_ARGS__), a
126# define REVERSE_5(a, ...) REVERSE_4(__VA_ARGS__), a
127# define REVERSE_6(a, ...) REVERSE_5(__VA_ARGS__), a
128# define REVERSE_7(a, ...) REVERSE_6(__VA_ARGS__), a
129# define REVERSE_8(a, ...) REVERSE_7(__VA_ARGS__), a
130# define REVERSE_9(a, ...) REVERSE_8(__VA_ARGS__), a
131# define REVERSE_10(a, ...) REVERSE_9(__VA_ARGS__), a
132# define REVERSE_11(a, ...) REVERSE_10(__VA_ARGS__), a
133# define REVERSE_12(a, ...) REVERSE_11(__VA_ARGS__), a
134# define REVERSE_13(a, ...) REVERSE_12(__VA_ARGS__), a
135# define REVERSE_14(a, ...) REVERSE_13(__VA_ARGS__), a
136# define REVERSE_15(a, ...) REVERSE_14(__VA_ARGS__), a
137# define REVERSE_16(a, ...) REVERSE_15(__VA_ARGS__), a
138# define REVERSE(...) \
139 ZTH_GET_MACRO_ARGN( \
140 0, ##__VA_ARGS__, REVERSE_16, REVERSE_15, REVERSE_14, REVERSE_13, REVERSE_12, \
141 REVERSE_11, REVERSE_10, REVERSE_9, REVERSE_8, REVERSE_7, REVERSE_6, REVERSE_5, \
142 REVERSE_4, REVERSE_3, REVERSE_2, REVERSE_1, REVERSE_0) \
143 (__VA_ARGS__)
144#endif
145
146#include <stdarg.h>
147
148EXTERN_C ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) void
149zth_logv(char const* fmt, va_list arg);
150
151#ifdef __cplusplus
152# include <cstdio>
153# include <cstdlib>
154# include <cstring>
155# include <limits>
156# include <memory>
157# include <string>
158# include <vector>
159
160# if __cplusplus >= 201103L
161# include <cinttypes>
162# else
163# include <inttypes.h>
164# endif
165
166# ifdef ZTH_HAVE_PTHREAD
167# include <pthread.h>
168# endif
169
170# ifdef ZTH_OS_WINDOWS
171# include <process.h>
172# else
173# include <sys/types.h>
174# include <unistd.h>
175# endif
176
177# ifdef __cplusplus
182# define ZTH_DBG_PREFIX " > "
183
194# define zth_dbg(group, fmt, a...) \
195 do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \
196 if(::zth::Config::SupportDebugPrint && ::zth::Config::Print_##group > 0 \
197 && zth_config(EnableDebugPrint)) { \
198 if(::zth::Config::EnableColorLog) \
199 ::zth::log_color( \
200 ::zth::Config::Print_##group, \
201 ZTH_DBG_PREFIX "zth::" ZTH_STRINGIFY(group) ": " fmt \
202 "\n", \
203 ##a); \
204 else \
205 ::zth::log( \
206 ZTH_DBG_PREFIX "zth::" ZTH_STRINGIFY(group) ": " fmt \
207 "\n", \
208 ##a); \
209 } \
210 } while(0)
211
216# if !defined(NDEBUG) && !defined(CPPCHECK)
217# define zth_assert(expr) \
218 do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \
219 if(unlikely(::zth::Config::EnableAssert && !(expr))) \
220 ::zth::assert_handler( \
221 __FILE__, __LINE__, \
222 ::zth::Config::EnableFullAssert ? ZTH_STRINGIFY(expr) \
223 : nullptr); \
224 } while(false)
225# else
226# define zth_assert(...) \
227 do { /* NOLINT(cppcoreguidelines-avoid-do-while) */ \
228 } while(0)
229# endif
230# endif
231
232# ifndef ZTH_CLASS_NOCOPY
233# if __cplusplus >= 201103L
234# define ZTH_CLASS_NOCOPY(Class) \
235 public: \
236 Class(Class const&) = delete; \
237 Class(Class&&) = \
238 delete; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) \
239 */ \
240 Class& operator=(Class const&) noexcept = delete; \
241 Class& operator=(Class&&) noexcept = \
242 delete; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) \
243 */ \
244 private:
245# else
246# define ZTH_CLASS_NOCOPY(Class) \
247 private: \
248 Class(Class const&); \
249 Class& operator=(Class const&);
250# endif
251# endif
252
253# include <libzth/zmq.h>
254#endif // __cplusplus
255
256#ifdef __cplusplus
257EXTERN_C ZTH_EXPORT __attribute__((noreturn)) void
258zth_assert_handler(char const* file, int line, char const* expr);
259
260namespace zth {
261ZTH_EXPORT ZTH_INLINE __attribute__((noreturn)) void
262assert_handler(char const* file, int line, char const* expr)
263{
264 zth_assert_handler(file, line, expr);
265}
266} // namespace zth
267#else
268ZTH_EXPORT __attribute__((noreturn)) void
269zth_assert_handler(char const* file, int line, char const* expr);
270#endif
271
272#ifdef __cplusplus
273namespace zth {
274
275ZTH_EXPORT __attribute__((returns_nonnull)) char const* banner() noexcept;
276
277ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2), noreturn)) void
278abort(char const* fmt, ...) noexcept;
279
280ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0), noreturn)) void
281abortv(char const* fmt, va_list args) noexcept;
282
283ZTH_EXPORT bool log_supports_ansi_colors() noexcept;
284
285ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 0))) void
286log_colorv(int color, char const* fmt, va_list args);
287
293ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) inline void
294log_color(int color, char const* fmt, ...)
295{
296 va_list args;
297 va_start(args, fmt);
298 log_colorv(color, fmt, args);
299 va_end(args);
300}
301
308ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) inline void
309logv(char const* fmt, va_list arg)
310{
311 ::zth_logv(fmt, arg);
312}
313
320ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline void log(char const* fmt, ...)
321{
322 va_list args;
323 va_start(args, fmt);
324 logv(fmt, args);
325 va_end(args);
326}
327
332typedef std::basic_string<char, std::char_traits<char>, Config::Allocator<char>::type> string;
333
342public:
343 cow_string()
344 : m_cstr()
345 {}
347 // cppcheck-suppress noExplicitConstructor
348 cow_string(char const* s)
349 : m_cstr(s)
350 {}
352 // cppcheck-suppress noExplicitConstructor
353 cow_string(string const& s)
354 : m_cstr()
355 , m_str(s)
356 {}
357
358 cow_string(cow_string const& s)
359 {
360 *this = s;
362
364 {
365 m_cstr = s.m_cstr;
366 m_str = s.m_str;
367 return *this;
369
370 cow_string& operator=(char const* s)
371 {
372 m_cstr = s;
373 m_str.clear();
374 return *this;
376
377 cow_string& operator=(string const& s)
378 {
379 m_cstr = nullptr;
380 m_str = s;
381 return *this;
382 }
384# if __cplusplus >= 201103L
385 cow_string(cow_string&& s) = default;
386 cow_string& operator=(cow_string&& s) = default;
388 // cppcheck-suppress noExplicitConstructor
389 cow_string(string&& s)
390 : m_cstr()
391 , m_str(std::move(s))
392 {}
393
394 cow_string& operator=(string&& s)
395 {
396 m_cstr = nullptr;
397 m_str = std::move(s);
398 return *this;
400
401 string str() &&
402 {
403 // cppcheck-suppress returnStdMoveLocal
404 return std::move(local());
405 }
406# endif
407
408 string const& str() const LREF_QUALIFIED
409 {
410 return local();
412
413 char const* c_str() const
414 {
415 return m_cstr ? m_cstr : m_str.c_str();
417
418 operator string const&() const
419 {
420 return str();
422
423 char const& at(size_t pos) const
424 {
425 return str().at(pos);
427
428 char operator[](size_t pos) const
429 {
430 return m_cstr ? m_cstr[pos] : str()[pos];
432
433 char& operator[](size_t pos)
434 {
435 return local()[pos];
437
438 char const* data() const
439 {
440 return c_str();
442
443 bool empty() const noexcept
444 {
445 return m_cstr ? *m_cstr == 0 : m_str.empty();
447
448 size_t size() const noexcept
449 {
450 return str().size();
452
453 size_t length() const noexcept
454 {
455 return str().length();
457
458 void clear() noexcept
459 {
460 m_cstr = nullptr;
461 m_str.clear();
463
464 bool isConst() const noexcept
465 {
466 return m_cstr != nullptr;
468
469 bool isLocal() const noexcept
470 {
471 return m_cstr == nullptr;
472 }
474protected:
475 string const& local() const
476 {
477 if(unlikely(m_cstr)) {
478 m_str = m_cstr;
479 m_cstr = nullptr;
480 }
481 return m_str;
483
484 string& local()
485 {
486 const_cast<cow_string const*>(this)->local();
487 return m_str;
488 }
489
490private:
491 mutable char const* m_cstr;
492 mutable string m_str;
493};
494
495__attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) string formatv(char const* fmt, va_list args);
496
500__attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline string format(char const* fmt, ...)
501{
502 va_list args;
503 va_start(args, fmt);
504 string res = formatv(fmt, args);
505 va_end(args);
506 return res;
507}
508
513template <typename T>
514inline cow_string str(T value)
515{
516 return cow_string(value);
517}
519template <>
520inline cow_string str<char>(char value)
521{
522 return format("%c", value);
523}
525template <>
526inline cow_string str<signed char>(signed char value)
527{
528 if(Config::UseLimitedFormatSpecifiers)
529 return format("%d", (int)value);
530 else
531 return format("%hhd", value);
532}
534template <>
535inline cow_string str<unsigned char>(unsigned char value)
536{
537 if(Config::UseLimitedFormatSpecifiers)
538 return format("%u", (unsigned int)value);
539 else
540 return format("%hhu", value);
541}
543template <>
544inline cow_string str<short>(short value)
545{
546 if(Config::UseLimitedFormatSpecifiers)
547 return format("%d", (int)value);
548 else
549 return format("%hd", value);
550}
552template <>
553inline cow_string str<unsigned short>(unsigned short value)
554{
555 if(Config::UseLimitedFormatSpecifiers)
556 return format("%u", (unsigned int)value);
557 else
558 return format("%hu", value);
559}
561template <>
562inline cow_string str<int>(int value)
563{
564 return format("%d", value);
565}
567template <>
568inline cow_string str<unsigned int>(unsigned int value)
569{
570 return format("%u", value);
571}
572
573namespace impl {
574ZTH_EXPORT ZTH_INLINE cow_string strull(unsigned long long value)
575{
577 if(value > (unsigned long long)std::numeric_limits<int>::max())
578 return format(
579 "0x%x%08x", (unsigned)(value >> 32U),
580 (unsigned)(value & 0xFFFFFFFFU));
581 else
582 return format("%d", (int)value);
583 } else {
584 return format("%llu", value);
585 }
586}
587} // namespace impl
589template <>
590inline cow_string str<long>(long value)
591{
592 if(Config::UseLimitedFormatSpecifiers) {
593 if(sizeof(long) == sizeof(int)
594 || (value <= std::numeric_limits<int>::max()
595 && value >= std::numeric_limits<int>::min()))
596 return format("%d", (int)value);
597 else
598 return impl::strull((unsigned long long)value);
599 } else {
600 return format("%ld", value);
601 }
602}
604template <>
605inline cow_string str<unsigned long>(unsigned long value)
606{
607 if(Config::UseLimitedFormatSpecifiers) {
608 if(sizeof(unsigned long) == sizeof(unsigned int)
609 || value <= std::numeric_limits<unsigned int>::max())
610 return format("%u", (unsigned int)value);
611 else
612 return impl::strull((unsigned long long)value);
613 } else {
614 return format("%lu", value);
615 }
616}
618template <>
619inline cow_string str<long long>(long long value)
620{
621 if(Config::UseLimitedFormatSpecifiers) {
622 if(value > std::numeric_limits<int>::max()
623 || value < std::numeric_limits<int>::min())
624 return impl::strull((unsigned long long)value);
625 else
626 return format("%d", (int)value);
627 } else {
628 return format("%lld", value);
629 }
630}
632template <>
633inline cow_string str<unsigned long long>(unsigned long long value)
634{
635 return impl::strull(value);
636}
637
638namespace impl {
639ZTH_EXPORT ZTH_INLINE cow_string strf(float value)
640{
642 if(!(value < (float)std::numeric_limits<int>::max()
643 && value > (float)-std::numeric_limits<int>::max()))
644 return "?range?";
645
646 int i = (int)value;
647 float fi = (float)i;
648 if(std::abs(fi - value) < 0.0005F)
649 return format("%d", i);
650
651 int f = std::min(std::abs((int)((value - fi) * 1000.0F + 0.5F)), 999);
652 return format("%d.%03d", i, f);
653 } else {
654 return format("%g", (double)value);
655 }
656}
657} // namespace impl
659template <>
660inline cow_string str<float>(float value)
661{
662 if(Config::UseLimitedFormatSpecifiers)
663 return impl::strf(value);
664 else
665 return format("%g", (double)value);
666}
668template <>
669inline cow_string str<double>(double value)
670{
671 if(Config::UseLimitedFormatSpecifiers)
672 return impl::strf((float)value);
673 else
674 return format("%g", value);
675}
677template <>
678inline cow_string str<long double>(long double value)
679{
680 if(Config::UseLimitedFormatSpecifiers)
681 return impl::strf((float)value);
682 else
683 return format("%Lg", value);
684}
686template <>
687inline cow_string str<void*>(void* value)
688{
689 return format("%p", value);
690}
691
692# if __cplusplus >= 201103L
693template <>
694inline cow_string str<string&&>(string&& value)
695{
696 return cow_string(std::move(value));
697}
698# endif
699
703inline string err(int e)
704{
705# ifdef ZTH_OS_BAREMETAL
706 // You are probably low on memory. Don't include all strerror strings in the binary.
707 return format("error %d", e);
708# elif defined(ZTH_HAVE_LIBZMQ)
709 return format("%s (error %d)", zmq_strerror(e), e);
710# elif ZTH_THREADS && !defined(ZTH_OS_WINDOWS)
711 char buf[128];
712# if !defined(ZTH_OS_LINUX) || (_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)
713 // XSI-compatible
714 return format("%s (error %d)", strerror_r(e, buf, sizeof(buf)) ? "Unknown error" : buf, e);
715# else
716 // GNU-specific
717 return format("%s (error %d)", strerror_r(e, buf, sizeof(buf)), e);
718# endif
719# else
720 // Not thread-safe
721 return format("%s (error %d)", strerror(e), e);
722# endif
724
726protected:
727 virtual char const* id_str() const noexcept = 0;
728 virtual ~UniqueIDBase() noexcept is_default
729 friend cow_string str<UniqueIDBase const&>(UniqueIDBase const&);
730};
732template <>
733inline cow_string str<UniqueIDBase const&>(UniqueIDBase const& value)
734{
735 return cow_string(value.id_str());
736}
737
738// In case names are enabled.
739template <bool Named = Config::NamedObjects>
741protected:
742 explicit NamedUniqueID(char const* UNUSED_PAR(name) = nullptr)
743 {
744 if(name)
745 m_name = name;
747
748 explicit NamedUniqueID(string const& name)
749 : m_name(name)
750 {}
752# if __cplusplus >= 201103L
753 explicit NamedUniqueID(string&& name)
754 : m_name(std::move(name))
755 {}
756# endif // C++11
757
758 virtual uint64_t id_() const noexcept = 0;
760public:
761 virtual ~NamedUniqueID() noexcept override is_default
762
763 string const& name() const noexcept
764 {
765 return m_name;
767
768 void setName(string const& name)
769 {
770 setName(name.c_str());
772
773 void setName(char const* name)
774 {
775 m_name = name;
776 m_id_str.clear();
777 changedName(this->name());
778 }
780# if __cplusplus >= 201103L
781 void setName(string&& name)
782 {
783 m_name = std::move(name);
784 m_id_str.clear();
785 changedName(this->name());
786 }
787# endif
788
789 virtual char const* id_str() const noexcept override
790 {
791 if(unlikely(m_id_str.empty())) {
792 try {
793 m_id_str =
794# ifdef ZTH_OS_BAREMETAL
795 // No OS, no pid. And if newlib is used, don't try to format
796 // 64-bit ints.
797 format("%s #%u", name().empty() ? "Object" : name().c_str(),
798 (unsigned int)id_());
799# else
800 format("%s #%u:%" PRIu64,
801 name().empty() ? "Object" : name().c_str(),
802# ifdef ZTH_OS_WINDOWS
803 (unsigned int)_getpid(),
804# else
805 (unsigned int)getpid(),
806# endif
807 id_());
808# endif
809 } catch(...) {
810 // Ignore exceptions here.
811 }
812
813 if(unlikely(m_id_str.empty()))
814 // Should not happen, but make sure the string is not empty.
815 m_id_str = "?";
816 }
817
818 return m_id_str.c_str();
819 }
820
821private:
822 virtual void changedName(string const& UNUSED_PAR(name)) {}
823
824private:
825 string m_name;
826 string mutable m_id_str;
827};
828
829// In case names are disabled.
830template <>
831class NamedUniqueID<false> : public UniqueIDBase {
832protected:
833 explicit NamedUniqueID(string const& UNUSED_PAR(name)) {}
834 explicit NamedUniqueID(char const* UNUSED_PAR(name) = nullptr) {}
835
836 virtual uint64_t id_() const noexcept = 0;
838public:
839 virtual ~NamedUniqueID() noexcept override is_default
840 string const& name() const noexcept
841 {
842 static string const null;
843 return null;
845
846 void setName(string const& UNUSED_PAR(name))
847 {
848 changedName(this->name());
850
851 void setName(char const* UNUSED_PAR(name))
852 {
853 changedName(this->name());
854 }
856# if __cplusplus >= 201103L
857 void setName(string&& UNUSED_PAR(name))
858 {
859 changedName(this->name());
860 }
861# endif
862
863 virtual char const* id_str() const noexcept override
864 {
865 return name().c_str();
866 }
867
868private:
869 virtual void changedName(string const& UNUSED_PAR(name)) {}
870};
871
876template <typename T, bool ThreadSafe = Config::EnableThreads>
877class UniqueID : public NamedUniqueID<> {
878# if __cplusplus < 201103L
879 ZTH_CLASS_NOCOPY(UniqueID)
880# endif // Pre C++11
881public:
882 typedef NamedUniqueID<> base;
884# if __cplusplus >= 201103L
885 UniqueID(UniqueID const&) = delete;
886 UniqueID& operator=(UniqueID const&) = delete;
887
888 UniqueID(UniqueID&& u) noexcept
889 : m_id{}
890 {
891 *this = std::move(u);
893
894 UniqueID& operator=(UniqueID&& u) noexcept
895 {
896 m_id = u.m_id;
897 u.m_id = 0;
898 base::operator=(std::move(u));
899 return *this;
900 }
901# endif // C++11
902public:
903 static uint64_t getID() noexcept
904 {
905 return ThreadSafe ?
906# if GCC_VERSION < 40802L
907 __sync_add_and_fetch(&m_nextId, 1)
908# else
909 __atomic_add_fetch(&m_nextId, 1, __ATOMIC_RELAXED)
910# endif
911 : ++m_nextId;
913
914 explicit UniqueID(string const& name)
915 : base(name)
916 , m_id(getID())
917 {}
919# if __cplusplus >= 201103L
920 explicit UniqueID(string&& name)
921 : base(std::move(name))
922 , m_id(getID())
923 {}
924# endif
925
926 explicit UniqueID(char const* name = nullptr)
927 : base(name)
928 , m_id(getID())
929 {}
930
931 virtual ~UniqueID() noexcept override is_default
932
933 void const* normptr() const noexcept
934 {
935 return this;
937
938 __attribute__((pure)) uint64_t id() const noexcept
939 {
940 return m_id;
941 }
943protected:
944 virtual uint64_t id_() const noexcept final
945 {
946 return id();
947 }
948
949private:
950 uint64_t m_id;
951 // If allocating once every ns, it takes more than 500 years until we run out of
952 // identifiers.
953 static uint64_t m_nextId;
954};
955
956template <typename T, bool ThreadSafe>
958
959class RefCounted {
961public:
962 RefCounted() noexcept
963 : m_count()
964 {}
965
966 virtual ~RefCounted() noexcept
967 {
968 zth_assert(m_count == 0);
970
971 void used() noexcept
972 {
973 zth_assert(m_count < std::numeric_limits<size_t>::max());
974 m_count++;
976
977 bool unused() noexcept
978 {
979 zth_assert(m_count > 0);
980 if(--m_count > 0)
981 return false;
982
983 cleanup();
984 return true;
986
987 size_t refs() const noexcept
988 {
989 return m_count;
990 }
992protected:
993 virtual void cleanup() noexcept
994 {
995 delete this;
996 }
997
998private:
999 size_t m_count;
1000};
1002template <typename T, typename WhenTIsVoid>
1003struct choose_type {
1004 typedef T type;
1005};
1007template <typename WhenTIsVoid>
1008struct choose_type<void, WhenTIsVoid> {
1009 typedef WhenTIsVoid type;
1010};
1011
1012# if __cplusplus >= 201103L
1013template <size_t...>
1014struct Sequence {};
1015# ifndef DOXYGEN
1016template <size_t N, size_t... S>
1017struct SequenceGenerator : SequenceGenerator<N - 1, N - 1, S...> {};
1018
1019template <size_t... S>
1020struct SequenceGenerator<0, S...> {
1021 typedef Sequence<S...> type;
1022};
1023# endif
1024# endif
1028# ifdef _DEBUG
1029template <typename T>
1031public:
1032 typedef safe_ptr type;
1033 typedef T pointer_type;
1035 // cppcheck-suppress noExplicitConstructor
1036 constexpr safe_ptr(pointer_type* p) noexcept
1037 : m_p(p)
1039
1040 constexpr operator pointer_type*() const noexcept
1041 {
1042 return ptr();
1044
1045 constexpr operator bool() const noexcept
1046 {
1047 return ptr();
1049
1050 constexpr14 pointer_type* operator->() const noexcept
1051 {
1052 zth_assert(ptr());
1053 return ptr();
1055
1056 constexpr14 pointer_type& operator*() const noexcept
1057 {
1058 zth_assert(ptr());
1059 // cppcheck-suppress nullPointerRedundantCheck
1060 return *ptr();
1061 }
1063protected:
1064 constexpr pointer_type* ptr() const noexcept
1065 {
1066 return m_p;
1067 }
1068
1069private:
1070 pointer_type* m_p;
1071};
1072# else // _DEBUG
1073
1074// No checking, use use the pointer.
1075template <typename T>
1076class safe_ptr {
1077public:
1078 typedef T* type;
1079};
1080# endif // !_DEBUG
1081
1104template <typename T>
1105class Singleton {
1106public:
1108 typedef T singleton_type;
1109
1110protected:
1115 constexpr14 Singleton() noexcept
1116 {
1117 // Do not enforce construction of only one Singleton, only register the first one
1118 // as 'the' instance.
1119
1120 if(!m_instance)
1121 m_instance = static_cast<singleton_type*>(this);
1122 }
1123
1128 ~Singleton()
1129 {
1130 if(m_instance == static_cast<singleton_type*>(this))
1131 m_instance = nullptr;
1132 }
1133
1134public:
1139 __attribute__((pure)) constexpr14 static typename safe_ptr<singleton_type>::type
1140 instance() noexcept
1141 {
1142 return m_instance;
1143 }
1144
1145private:
1147 static singleton_type* m_instance;
1148};
1149
1150template <typename T>
1151typename Singleton<T>::singleton_type* Singleton<T>::m_instance = nullptr;
1152
1162template <typename T>
1163class ThreadLocalSingleton {
1164public:
1166 typedef T singleton_type;
1167
1168protected:
1174 __attribute__((no_sanitize_undefined)) ThreadLocalSingleton()
1175 {
1176 // Do not enforce construction of only one Singleton, only register the first one
1177 // as 'the' instance.
1178
1179 if(!ZTH_TLS_GET(m_instance))
1180 ZTH_TLS_SET(m_instance, static_cast<singleton_type*>(this));
1181 }
1182
1187 __attribute__((no_sanitize_undefined)) ~ThreadLocalSingleton()
1188 {
1189 if(ZTH_TLS_GET(m_instance) == static_cast<singleton_type*>(this))
1190 ZTH_TLS_SET(m_instance, nullptr);
1191 }
1192
1193public:
1198 __attribute__((pure)) static typename safe_ptr<singleton_type>::type instance() noexcept
1199 {
1200 return ZTH_TLS_GET(m_instance);
1201 }
1202
1203private:
1205 ZTH_TLS_MEMBER(singleton_type*, m_instance)
1206};
1207
1208template <typename T>
1210 typename ThreadLocalSingleton<T>::singleton_type*, ThreadLocalSingleton<T>::m_instance,
1211 nullptr)
1212
1213
1229template <typename T, int8_t Prealloc = 1, typename Allocator = typename Config::Allocator<T>::type>
1231public:
1232 typedef T value_type;
1233 typedef Allocator allocator_type;
1234 typedef std::vector<value_type, allocator_type> vector_type;
1235
1236 enum {
1237 prealloc_request = Prealloc > 0 ? Prealloc : 0,
1238 prealloc_request_size = prealloc_request * sizeof(value_type),
1239 vector_size = sizeof(vector_type),
1240 buffer_size =
1241 prealloc_request_size > vector_size ? prealloc_request_size : vector_size,
1242 prealloc = buffer_size / sizeof(value_type),
1243 };
1244
1248 // cppcheck-suppress uninitMemberVar
1249 constexpr small_vector() noexcept
1250 : m_size()
1251 {
1252 static_assert(prealloc < std::numeric_limits<uint8_t>::max(), "");
1253 }
1254
1258 ~small_vector()
1259 {
1260 if(is_small())
1261 resize(0);
1262 else
1263 vector().~vector_type();
1264 }
1265
1269 value_type& operator[](size_t index) noexcept
1270 {
1271 zth_assert(index < size());
1272 return data()[index];
1273 }
1274
1278 value_type const& operator[](size_t index) const noexcept
1279 {
1280 zth_assert(index < size());
1281 return data()[index];
1282 }
1283
1289 value_type& front() noexcept
1290 {
1291 return (*this)[0];
1292 }
1293
1299 value_type const& front() const noexcept
1300 {
1301 return (*this)[0];
1302 }
1303
1309 value_type& back() noexcept
1310 {
1311 zth_assert(!empty());
1312 return (*this)[size() - 1U];
1313 }
1314
1320 value_type const& back() const noexcept
1321 {
1322 zth_assert(!empty());
1323 return (*this)[size() - 1U];
1324 }
1325
1329 value_type* data() noexcept
1330 {
1331 return is_small() ? array() : vector().data();
1332 }
1333
1337 value_type const* data() const noexcept
1338 {
1339 return is_small() ? array() : vector().data();
1340 }
1341
1345 bool empty() const noexcept
1346 {
1347 return is_small() ? m_size == 0 : vector().empty();
1348 }
1349
1353 size_t size() const noexcept
1354 {
1355 return is_small() ? m_size : vector().size();
1356 }
1357
1363 void reserve(size_t new_cap)
1364 {
1365 if(is_small() && new_cap <= prealloc)
1366 return;
1367
1368 if(is_small())
1369 make_vector(new_cap);
1370
1371 vector().reserve(new_cap);
1372 }
1373
1378 size_t capacity() const noexcept
1379 {
1380 return is_small() ? (size_t)prealloc : vector().capacity();
1381 }
1382
1388 void clear() noexcept
1389 {
1390 if(is_small())
1391 resize(0);
1392 else
1393 vector().clear();
1394 }
1395
1399 void clear_and_release() noexcept
1400 {
1401 if(is_small()) {
1402 resize(0);
1403 } else {
1404 vector_type v;
1405 v.swap(vector());
1406 }
1407 }
1408
1413 void push_back(value_type const& v)
1414 {
1415 reserve(size() + 1U);
1416
1417 if(m_size < prealloc) {
1418 new(&array()[m_size]) value_type(v);
1419 m_size++;
1420 } else
1421 vector().push_back(v);
1422 }
1423
1424# if __cplusplus >= 201103L
1429 template <class... Args>
1430 void emplace_back(Args&&... args)
1431 {
1432 reserve(size() + 1U);
1433
1434 if(m_size < prealloc) {
1435 new(&array()[m_size]) value_type(std::forward<Args>(args)...);
1436 m_size++;
1437 } else
1438 vector().emplace_back(std::forward<Args>(args)...);
1439 }
1440# endif
1441
1447 void pop_back() noexcept
1448 {
1449 zth_assert(!empty());
1450 if(is_small())
1451 array()[--m_size].~value_type();
1452 else
1453 vector().pop_back();
1454 }
1455
1461 void resize(size_t count, value_type const& x = value_type())
1462 {
1463 if(is_small()) {
1464 while(count < m_size)
1465 array()[--m_size].~value_type();
1466
1467 if(count <= prealloc) {
1468 while(count > m_size) {
1469 new(&array()[m_size]) value_type(x);
1470 m_size++;
1471 }
1472 } else {
1473 make_vector(count);
1474 vector().resize(count, x);
1475 }
1476 } else
1477 vector().resize(count, x);
1478 }
1479
1480protected:
1484 bool is_small() const noexcept
1485 {
1486 return m_size <= prealloc;
1487 }
1488
1492 value_type* array() noexcept
1493 {
1494 zth_assert(is_small());
1495 return reinterpret_cast<value_type*>(m_buffer);
1496 }
1497
1501 value_type const* array() const noexcept
1502 {
1503 zth_assert(is_small());
1504 return reinterpret_cast<value_type const*>(m_buffer);
1505 }
1506
1510 vector_type& vector() noexcept
1511 {
1512 zth_assert(!is_small());
1513 void* buffer = m_buffer;
1514 return *reinterpret_cast<vector_type*>(buffer);
1515 }
1516
1520 vector_type const& vector() const noexcept
1521 {
1522 zth_assert(!is_small());
1523 void const* buffer = m_buffer;
1524 return *reinterpret_cast<vector_type const*>(buffer);
1525 }
1526
1530 void make_vector(size_t new_cap)
1531 {
1532 if(!is_small())
1533 return;
1534
1535 vector_type v;
1536 v.reserve(std::max(new_cap, (size_t)m_size));
1537
1538 for(size_t i = 0; i < m_size; i++) {
1539# if __cplusplus >= 201103L
1540 v.emplace_back(std::move(array()[i]));
1541# else
1542 v.push_back(array()[i]);
1543# endif
1544 array()[i].~value_type();
1545 }
1546
1547 m_size = prealloc + 1U;
1548 new(m_buffer) vector_type;
1549 vector().swap(v);
1550 }
1551
1552private:
1557 alignas(vector_type) alignas(value_type) char m_buffer[buffer_size];
1558
1566 uint8_t m_size;
1567};
1569template <size_t size>
1570struct smallest_uint_size {};
1571
1572template <
1573 uint64_t x, typename size = smallest_uint_size<
1574 x >= 0x100000000ULL ? 8U
1575 : x >= 0x10000U ? 4U
1576 : x >= 0x100U ? 2U
1577 : 1U> >
1578struct smallest_uint {
1579 typedef uint64_t type;
1580};
1582template <uint64_t x>
1583struct smallest_uint<x, smallest_uint_size<1> > {
1584 typedef uint8_t type;
1585};
1587template <uint64_t x>
1588struct smallest_uint<x, smallest_uint_size<2> > {
1589 typedef uint16_t type;
1590};
1592template <uint64_t x>
1593struct smallest_uint<x, smallest_uint_size<4> > {
1594 typedef uint32_t type;
1595};
1596
1597
1598# if __cplusplus >= 201703L
1599// Based on https://gist.github.com/utilForever/1a058050b8af3ef46b58bcfa01d5375d
1600namespace impl {
1601template <class T, class... TArgs>
1602decltype(void(T{std::declval<TArgs>()...}), std::true_type{}) test_is_braces_constructible(int);
1604template <class, class...>
1605std::false_type test_is_braces_constructible(...);
1607template <class T, class... TArgs>
1609
1610struct any_type {
1611 template <class T>
1612 // cppcheck-suppress noExplicitConstructor
1613 constexpr operator T();
1614};
1615} // namespace impl
1617template <class T>
1618auto to_tuple(T&& object) noexcept
1619{
1620 using type = std::decay_t<T>;
1621
1622 // Repeat as required...
1623 if constexpr(
1626 auto&& [p1, p2, p3, p4] = std::forward<T>(object);
1627 return std::make_tuple(p1, p2, p3, p4);
1628 } else if constexpr(
1631 auto&& [p1, p2, p3] = std::forward<T>(object);
1632 return std::make_tuple(p1, p2, p3);
1634 auto&& [p1, p2] = std::forward<T>(object);
1635 return std::make_tuple(p1, p2);
1637 auto&& [p1] = std::forward<T>(object);
1638 return std::make_tuple(p1);
1639 } else {
1640 return std::make_tuple();
1641 }
1643
1644# define ZTH_STRUCTURED_BINDING_FORWARDING(Class) \
1645 template <typename T> \
1646 struct std::tuple_size<Class<T>> : std::tuple_size<T> {}; \
1647 \
1648 template <size_t N, typename T> \
1649 struct std::tuple_element<N, Class<T>> : std::tuple_element<N, T> {}; \
1650 \
1651 template <size_t N, typename T> \
1652 decltype(auto) get(Class<T>& f) noexcept \
1653 { \
1654 auto& x = to_tuple(*f); \
1655 return get<N>(x); \
1656 }
1657# define ZTH_STRUCTURED_BINDING_FORWARDING_NS(Class) \
1658 } /* assume namespace zth */ \
1659 ZTH_STRUCTURED_BINDING_FORWARDING(zth::Class) \
1660 namespace zth {
1661# else
1662# define ZTH_STRUCTURED_BINDING_FORWARDING(Class)
1663# define ZTH_STRUCTURED_BINDING_FORWARDING_NS(Class)
1664# endif // C++17
1665
1666
1667} // namespace zth
1668
1669#endif // __cplusplus
1670
1676#ifdef __cplusplus
1677EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((returns_nonnull)) char const* zth_banner()
1678{
1679 return zth::banner();
1680}
1681#else
1682ZTH_EXPORT __attribute__((returns_nonnull)) char const* zth_banner();
1683#endif
1684
1690EXTERN_C ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2), noreturn)) void
1691zth_abort(char const* fmt, ...);
1692
1693EXTERN_C ZTH_EXPORT __attribute__((noreturn)) void zth_terminate();
1694
1700#ifdef __cplusplus
1701EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) void
1702zth_log_color(int color, char const* fmt, ...)
1703{
1704 va_list args;
1705 va_start(args, fmt);
1706 zth::log_colorv(color, fmt, args);
1707 va_end(args);
1708}
1709#else
1710ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) void
1711zth_log_color(int color, char const* fmt, ...);
1712#endif
1713
1719#ifdef __cplusplus
1720EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 2, 0))) void
1721zth_log_colorv(int color, char const* fmt, va_list args)
1722{
1723 zth::log_colorv(color, fmt, args);
1724}
1725#else
1726ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 0))) void
1727zth_log_colorv(int color, char const* fmt, va_list args);
1728#endif
1729
1735#ifdef __cplusplus
1736EXTERN_C ZTH_EXPORT ZTH_INLINE
1737 __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void zth_log(char const* fmt, ...)
1738{
1739 va_list args;
1740 va_start(args, fmt);
1741 zth::logv(fmt, args);
1742 va_end(args);
1743}
1744#else
1745ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void zth_log(char const* fmt, ...);
1746#endif
1747
1748#if !defined(__clang_analyzer__) && GCC_VERSION >= 110000L
1749# ifndef __cplusplus
1750# include <stdlib.h>
1751# endif
1752# define ZTH_ERR_ATTR __attribute__((malloc(free), warn_unused_result))
1753#else
1754# define ZTH_ERR_ATTR __attribute__((malloc, warn_unused_result))
1755#endif
1756#ifdef __cplusplus
1757# include <cstring>
1758#endif
1768#ifdef __cplusplus
1769EXTERN_C ZTH_EXPORT __attribute__((warn_unused_result)) ZTH_INLINE char* zth_err(int e)
1770{
1771 zth::string s = zth::err(e);
1772 // cppcheck-suppress cstyleCast
1773 char* p = (char*)malloc(s.size() + 1); // NOLINT
1774 if(!p)
1775 return nullptr;
1776 memcpy(p, s.c_str(), s.size() + 1);
1777 return p;
1778}
1779#else
1780ZTH_EXPORT ZTH_ERR_ATTR char* zth_err(int e);
1781#endif
1782#undef ZTH_ERR_ATTR
1783
1784#ifdef ZTH_OS_BAREMETAL
1785// newlib probably doesn't have these. Provide some default implementation for
1786// them.
1787EXTERN_C __attribute__((format(gnu_printf, 2, 0))) int asprintf(char** strp, const char* fmt, ...);
1788EXTERN_C __attribute__((format(gnu_printf, 2, 0))) int
1789vasprintf(char** strp, const char* fmt, va_list ap);
1790#endif
1791
1792#endif // ZTH_UTIL_H
Singleton pattern.
Definition util.h:1103
T singleton_type
Alias of the T template parameter.
Definition util.h:1106
Singleton pattern, but only per-thread.
Definition util.h:1161
T singleton_type
Alias of the T template parameter.
Definition util.h:1164
virtual char const * id_str() const noexcept=0
friend cow_string str(UniqueIDBase const &)
Keeps track of a process-wide unique ID within the type T.
Definition util.h:875
Copy-on-write string.
Definition util.h:339
char operator[](size_t pos) const
Definition util.h:426
string str() &&
Definition util.h:399
bool isConst() const noexcept
Definition util.h:462
string const & local() const
Definition util.h:473
char const & at(size_t pos) const
Definition util.h:421
char const * c_str() const
Definition util.h:411
size_t length() const noexcept
Definition util.h:451
cow_string & operator=(cow_string const &s)
Definition util.h:361
size_t size() const noexcept
Definition util.h:446
char const * data() const
Definition util.h:436
void clear() noexcept
Definition util.h:456
bool empty() const noexcept
Definition util.h:441
bool isLocal() const noexcept
Definition util.h:467
Wrapper for a pointer, which checks validity of the pointer upon dereference.
Definition util.h:1028
A simple std::vector, which can contain Prealloc without heap allocation.
Definition util.h:1228
Allocator allocator_type
Definition util.h:1231
std::vector< value_type, allocator_type > vector_type
Definition util.h:1232
void zth_log(char const *fmt,...)
Logs a given printf()-like formatted string.
Definition util.h:1735
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:342
char * zth_err(int e)
Return a string like strerror() does, but as a zth::string.
Definition util.h:1767
void zth_log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
Definition util.h:1700
void zth_logv(char const *fmt, va_list arg)
Prints the given printf()-like formatted string to stdout.
Definition zth_logv.cpp:19
void zth_log_colorv(int color, char const *fmt, va_list args)
Logs a given printf()-like formatted string using an ANSI color code.
Definition util.h:1719
char const * zth_banner()
Returns a banner line with version and configuration information.
Definition util.h:1675
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:241
std::basic_string< char, std::char_traits< char >, Config::Allocator< char >::type > string
std::string type using Config::Allocator::type.
Definition util.h:330
char const * banner() noexcept
Returns a banner line with version and configuration information.
Definition util.cpp:38
void logv(char const *fmt, va_list arg)
Logs a given printf()-like formatted string.
Definition util.h:307
#define constexpr14
Definition macros.h:219
#define ZTH_TLS_DEFINE(type, var, init)
Definition macros.h:101
#define ZTH_TLS_GET(var)
Definition macros.h:106
#define is_default
Definition macros.h:223
#define ZTH_TLS_MEMBER(type, var)
Definition macros.h:104
#define ZTH_TLS_SET(var, value)
Definition macros.h:105
#define LREF_QUALIFIED
Definition macros.h:226
#define ZTH_ATTR_PRINTF
Definition macros.h:64
#define ZTH_INLINE
Definition macros.h:139
#define UNUSED_PAR(name)
Definition macros.h:79
STL namespace.
decltype(test_is_braces_constructible< T, TArgs... >(0)) is_braces_constructible
Definition util.h:1606
decltype(void(T{std::declval< TArgs >()...}), std::true_type{}) test_is_braces_constructible(int)
auto to_tuple(T &&object) noexcept
Definition util.h:1616
cow_string str(T value)
Returns an zth::string representation of the given value.
Definition util.h:512
string formatv(char const *fmt, va_list args)
Format like vsprintf(), but save the result in an zth::string.
Definition util.cpp:259
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition util.h:701
void assert_handler(char const *file, int line, char const *expr)
Definition util.h:260
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:498
static bool const UseLimitedFormatSpecifiers
Use limited formatting specifiers.
Definition config.h:297
Change the name of a fiber returned by zth_async.
Definition async.h:202
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:217
#define ZTH_ERR_ATTR
Definition util.h:1752
void zth_assert_handler(char const *file, int line, char const *expr)
#define ZTH_CLASS_NOCOPY(Class)
Definition util.h:234
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:60
void zth_terminate()
Terminate immediately.