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
148#ifdef __cplusplus
149# include <cstdio>
150# include <cstdlib>
151# include <cstring>
152# include <limits>
153# include <memory>
154# include <string>
155# include <vector>
156
157# if __cplusplus >= 201103L
158# include <cinttypes>
159# else
160# include <inttypes.h>
161# endif
162
163# ifdef ZTH_HAVE_PTHREAD
164# include <pthread.h>
165# endif
166
167# ifdef ZTH_OS_WINDOWS
168# include <process.h>
169# else
170# include <sys/types.h>
171# include <unistd.h>
172# endif
173
174EXTERN_C ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) void
175zth_logv(char const* fmt, va_list arg);
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# ifndef NDEBUG
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
255namespace zth {
256
257ZTH_EXPORT char const* banner() noexcept;
258
259ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2), noreturn)) void
260abort(char const* fmt, ...) noexcept;
261
262ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0), noreturn)) void
263abortv(char const* fmt, va_list args) noexcept;
264
265ZTH_EXPORT __attribute__((noreturn)) void
266assert_handler(char const* file, int line, char const* expr);
267
268ZTH_EXPORT bool log_supports_ansi_colors() noexcept;
269
270ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 0))) void
271log_colorv(int color, char const* fmt, va_list args);
272
278ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) inline void
279log_color(int color, char const* fmt, ...)
280{
281 va_list args;
282 va_start(args, fmt);
283 log_colorv(color, fmt, args);
284 va_end(args);
285}
286
293ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) inline void
294logv(char const* fmt, va_list arg)
295{
296 ::zth_logv(fmt, arg);
297}
298
305ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline void log(char const* fmt, ...)
306{
307 va_list args;
308 va_start(args, fmt);
309 logv(fmt, args);
310 va_end(args);
311}
312
317typedef std::basic_string<char, std::char_traits<char>, Config::Allocator<char>::type> string;
318
327public:
328 cow_string()
329 : m_cstr()
330 {}
332 // cppcheck-suppress noExplicitConstructor
333 cow_string(char const* s)
334 : m_cstr(s)
335 {}
337 // cppcheck-suppress noExplicitConstructor
338 cow_string(string const& s)
339 : m_cstr()
340 , m_str(s)
341 {}
342
343 cow_string(cow_string const& s)
344 {
345 *this = s;
347
349 {
350 m_cstr = s.m_cstr;
351 m_str = s.m_str;
352 return *this;
354
355 cow_string& operator=(char const* s)
356 {
357 m_cstr = s;
358 m_str.clear();
359 return *this;
361
362 cow_string& operator=(string const& s)
363 {
364 m_cstr = nullptr;
365 m_str = s;
366 return *this;
367 }
369# if __cplusplus >= 201103L
370 cow_string(cow_string&& s) = default;
371 cow_string& operator=(cow_string&& s) = default;
373 // cppcheck-suppress noExplicitConstructor
374 cow_string(string&& s)
375 : m_cstr()
376 , m_str(std::move(s))
377 {}
378
379 cow_string& operator=(string&& s)
380 {
381 m_cstr = nullptr;
382 m_str = std::move(s);
383 return *this;
385
386 string str() &&
387 {
388 // cppcheck-suppress returnStdMoveLocal
389 return std::move(local());
390 }
391# endif
392
393 string const& str() const LREF_QUALIFIED
394 {
395 return local();
397
398 char const* c_str() const
399 {
400 return m_cstr ? m_cstr : m_str.c_str();
402
403 operator string const&() const
404 {
405 return str();
407
408 char const& at(size_t pos) const
409 {
410 return str().at(pos);
412
413 char operator[](size_t pos) const
414 {
415 return m_cstr ? m_cstr[pos] : str()[pos];
417
418 char& operator[](size_t pos)
419 {
420 return local()[pos];
422
423 char const* data() const
424 {
425 return c_str();
427
428 bool empty() const noexcept
429 {
430 return m_cstr ? *m_cstr == 0 : m_str.empty();
432
433 size_t size() const noexcept
434 {
435 return str().size();
437
438 size_t length() const noexcept
439 {
440 return str().length();
442
443 void clear() noexcept
444 {
445 m_cstr = nullptr;
446 m_str.clear();
448
449 bool isConst() const noexcept
450 {
451 return m_cstr != nullptr;
453
454 bool isLocal() const noexcept
455 {
456 return m_cstr == nullptr;
457 }
459protected:
460 string const& local() const
461 {
462 if(unlikely(m_cstr)) {
463 m_str = m_cstr;
464 m_cstr = nullptr;
465 }
466 return m_str;
468
469 string& local()
470 {
471 const_cast<cow_string const*>(this)->local();
472 return m_str;
473 }
474
475private:
476 mutable char const* m_cstr;
477 mutable string m_str;
478};
479
480__attribute__((format(ZTH_ATTR_PRINTF, 1, 0))) string formatv(char const* fmt, va_list args);
481
485__attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) inline string format(char const* fmt, ...)
486{
487 va_list args;
488 va_start(args, fmt);
489 string res = formatv(fmt, args);
490 va_end(args);
491 return res;
492}
493
498template <typename T>
499inline cow_string str(T value)
500{
501 return cow_string(value);
502}
504template <>
505inline cow_string str<char>(char value)
506{
507 return format("%c", value);
508}
510template <>
511inline cow_string str<signed char>(signed char value)
512{
513 if(Config::UseLimitedFormatSpecifiers)
514 return format("%d", (int)value);
515 else
516 return format("%hhd", value);
517}
519template <>
520inline cow_string str<unsigned char>(unsigned char value)
521{
522 if(Config::UseLimitedFormatSpecifiers)
523 return format("%u", (unsigned int)value);
524 else
525 return format("%hhu", value);
526}
528template <>
529inline cow_string str<short>(short value)
530{
531 if(Config::UseLimitedFormatSpecifiers)
532 return format("%d", (int)value);
533 else
534 return format("%hd", value);
535}
537template <>
538inline cow_string str<unsigned short>(unsigned short value)
539{
540 if(Config::UseLimitedFormatSpecifiers)
541 return format("%u", (unsigned int)value);
542 else
543 return format("%hu", value);
544}
546template <>
547inline cow_string str<int>(int value)
548{
549 return format("%d", value);
550}
552template <>
553inline cow_string str<unsigned int>(unsigned int value)
554{
555 return format("%u", value);
556}
557
558namespace impl {
559ZTH_EXPORT ZTH_INLINE cow_string strull(unsigned long long value)
560{
562 if(value > (unsigned long long)std::numeric_limits<int>::max())
563 return format(
564 "0x%x%08x", (unsigned)(value >> 32U),
565 (unsigned)(value & 0xFFFFFFFFU));
566 else
567 return format("%d", (int)value);
568 } else {
569 return format("%llu", value);
570 }
571}
572} // namespace impl
574template <>
575inline cow_string str<long>(long value)
576{
577 if(Config::UseLimitedFormatSpecifiers) {
578 if(sizeof(long) == sizeof(int)
579 || (value <= std::numeric_limits<int>::max()
580 && value >= std::numeric_limits<int>::min()))
581 return format("%d", (int)value);
582 else
583 return impl::strull((unsigned long long)value);
584 } else {
585 return format("%ld", value);
586 }
587}
589template <>
590inline cow_string str<unsigned long>(unsigned long value)
591{
592 if(Config::UseLimitedFormatSpecifiers) {
593 if(sizeof(unsigned long) == sizeof(unsigned int)
594 || value <= std::numeric_limits<unsigned int>::max())
595 return format("%u", (unsigned int)value);
596 else
597 return impl::strull((unsigned long long)value);
598 } else {
599 return format("%lu", value);
600 }
601}
603template <>
604inline cow_string str<long long>(long long value)
605{
606 if(Config::UseLimitedFormatSpecifiers) {
607 if(value > std::numeric_limits<int>::max()
608 || value < std::numeric_limits<int>::min())
609 return impl::strull((unsigned long long)value);
610 else
611 return format("%d", (int)value);
612 } else {
613 return format("%lld", value);
614 }
615}
617template <>
618inline cow_string str<unsigned long long>(unsigned long long value)
619{
620 return impl::strull(value);
621}
622
623namespace impl {
624ZTH_EXPORT ZTH_INLINE cow_string strf(float value)
625{
627 if(!(value < (float)std::numeric_limits<int>::max()
628 && value > (float)-std::numeric_limits<int>::max()))
629 return "?range?";
630
631 int i = (int)value;
632 float fi = (float)i;
633 if(std::abs(fi - value) < 0.0005F)
634 return format("%d", i);
635
636 int f = std::min(std::abs((int)((value - fi) * 1000.0F + 0.5F)), 999);
637 return format("%d.%03d", i, f);
638 } else {
639 return format("%g", (double)value);
640 }
641}
642} // namespace impl
644template <>
645inline cow_string str<float>(float value)
646{
647 if(Config::UseLimitedFormatSpecifiers)
648 return impl::strf(value);
649 else
650 return format("%g", (double)value);
651}
653template <>
654inline cow_string str<double>(double value)
655{
656 if(Config::UseLimitedFormatSpecifiers)
657 return impl::strf((float)value);
658 else
659 return format("%g", value);
660}
662template <>
663inline cow_string str<long double>(long double value)
664{
665 if(Config::UseLimitedFormatSpecifiers)
666 return impl::strf((float)value);
667 else
668 return format("%Lg", value);
669}
671template <>
672inline cow_string str<void*>(void* value)
673{
674 return format("%p", value);
675}
676
677# if __cplusplus >= 201103L
678template <>
679inline cow_string str<string&&>(string&& value)
680{
681 return cow_string(std::move(value));
682}
683# endif
684
688inline string err(int e)
689{
690# ifdef ZTH_OS_BAREMETAL
691 // You are probably low on memory. Don't include all strerror strings in the binary.
692 return format("error %d", e);
693# elif defined(ZTH_HAVE_LIBZMQ)
694 return format("%s (error %d)", zmq_strerror(e), e);
695# elif ZTH_THREADS && !defined(ZTH_OS_WINDOWS)
696 char buf[128];
697# if !defined(ZTH_OS_LINUX) || (_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)
698 // XSI-compatible
699 return format("%s (error %d)", strerror_r(e, buf, sizeof(buf)) ? "Unknown error" : buf, e);
700# else
701 // GNU-specific
702 return format("%s (error %d)", strerror_r(e, buf, sizeof(buf)), e);
703# endif
704# else
705 // Not thread-safe
706 return format("%s (error %d)", strerror(e), e);
707# endif
709
711protected:
712 virtual char const* id_str() const noexcept = 0;
713 virtual ~UniqueIDBase() noexcept is_default
714 friend cow_string str<UniqueIDBase const&>(UniqueIDBase const&);
715};
717template <>
718inline cow_string str<UniqueIDBase const&>(UniqueIDBase const& value)
719{
720 return cow_string(value.id_str());
721}
722
723// In case names are enabled.
724template <bool Named = Config::NamedObjects>
726protected:
727 explicit NamedUniqueID(char const* UNUSED_PAR(name) = nullptr)
728 {
729 if(name)
730 m_name = name;
732
733 explicit NamedUniqueID(string const& name)
734 : m_name(name)
735 {}
737# if __cplusplus >= 201103L
738 explicit NamedUniqueID(string&& name)
739 : m_name(std::move(name))
740 {}
741# endif // C++11
742
743 virtual uint64_t id_() const noexcept = 0;
745public:
746 virtual ~NamedUniqueID() noexcept override is_default
747
748 string const& name() const noexcept
749 {
750 return m_name;
752
753 void setName(string const& name)
754 {
755 setName(name.c_str());
757
758 void setName(char const* name)
759 {
760 m_name = name;
761 m_id_str.clear();
762 changedName(this->name());
763 }
765# if __cplusplus >= 201103L
766 void setName(string&& name)
767 {
768 m_name = std::move(name);
769 m_id_str.clear();
770 changedName(this->name());
771 }
772# endif
773
774 virtual char const* id_str() const noexcept override
775 {
776 if(unlikely(m_id_str.empty())) {
777 try {
778 m_id_str =
779# ifdef ZTH_OS_BAREMETAL
780 // No OS, no pid. And if newlib is used, don't try to format
781 // 64-bit ints.
782 format("%s #%u", name().empty() ? "Object" : name().c_str(),
783 (unsigned int)id_());
784# else
785 format("%s #%u:%" PRIu64,
786 name().empty() ? "Object" : name().c_str(),
787# ifdef ZTH_OS_WINDOWS
788 (unsigned int)_getpid(),
789# else
790 (unsigned int)getpid(),
791# endif
792 id_());
793# endif
794 } catch(...) {
795 // Ignore exceptions here.
796 }
797
798 if(unlikely(m_id_str.empty()))
799 // Should not happen, but make sure the string is not empty.
800 m_id_str = "?";
801 }
802
803 return m_id_str.c_str();
804 }
805
806private:
807 virtual void changedName(string const& UNUSED_PAR(name)) {}
808
809private:
810 string m_name;
811 string mutable m_id_str;
812};
813
814// In case names are disabled.
815template <>
816class NamedUniqueID<false> : public UniqueIDBase {
817protected:
818 explicit NamedUniqueID(string const& UNUSED_PAR(name)) {}
819 explicit NamedUniqueID(char const* UNUSED_PAR(name) = nullptr) {}
820
821 virtual uint64_t id_() const noexcept = 0;
823public:
824 virtual ~NamedUniqueID() noexcept override is_default
825 string const& name() const noexcept
826 {
827 static string const null;
828 return null;
830
831 void setName(string const& UNUSED_PAR(name))
832 {
833 changedName(this->name());
835
836 void setName(char const* UNUSED_PAR(name))
837 {
838 changedName(this->name());
839 }
841# if __cplusplus >= 201103L
842 void setName(string&& UNUSED_PAR(name))
843 {
844 changedName(this->name());
845 }
846# endif
847
848 virtual char const* id_str() const noexcept override
849 {
850 return name().c_str();
851 }
852
853private:
854 virtual void changedName(string const& UNUSED_PAR(name)) {}
855};
856
861template <typename T, bool ThreadSafe = Config::EnableThreads>
862class UniqueID : public NamedUniqueID<> {
863# if __cplusplus < 201103L
864 ZTH_CLASS_NOCOPY(UniqueID)
865# endif // Pre C++11
866public:
867 typedef NamedUniqueID<> base;
869# if __cplusplus >= 201103L
870 UniqueID(UniqueID const&) = delete;
871 UniqueID& operator=(UniqueID const&) = delete;
872
873 UniqueID(UniqueID&& u) noexcept
874 : m_id{}
875 {
876 *this = std::move(u);
878
879 UniqueID& operator=(UniqueID&& u) noexcept
880 {
881 m_id = u.m_id;
882 u.m_id = 0;
883 base::operator=(std::move(u));
884 return *this;
885 }
886# endif // C++11
887public:
888 static uint64_t getID() noexcept
889 {
890 return ThreadSafe ?
891# if GCC_VERSION < 40802L
892 __sync_add_and_fetch(&m_nextId, 1)
893# else
894 __atomic_add_fetch(&m_nextId, 1, __ATOMIC_RELAXED)
895# endif
896 : ++m_nextId;
898
899 explicit UniqueID(string const& name)
900 : base(name)
901 , m_id(getID())
902 {}
904# if __cplusplus >= 201103L
905 explicit UniqueID(string&& name)
906 : base(std::move(name))
907 , m_id(getID())
908 {}
909# endif
910
911 explicit UniqueID(char const* name = nullptr)
912 : base(name)
913 , m_id(getID())
914 {}
915
916 virtual ~UniqueID() noexcept override is_default
917
918 void const* normptr() const noexcept
919 {
920 return this;
922
923 __attribute__((pure)) uint64_t id() const noexcept
924 {
925 return m_id;
926 }
928protected:
929 virtual uint64_t id_() const noexcept final
930 {
931 return id();
932 }
933
934private:
935 uint64_t m_id;
936 // If allocating once every ns, it takes more than 500 years until we run out of
937 // identifiers.
938 static uint64_t m_nextId;
939};
940
941template <typename T, bool ThreadSafe>
943
944class RefCounted {
946public:
947 RefCounted() noexcept
948 : m_count()
949 {}
950
951 virtual ~RefCounted() noexcept
952 {
953 zth_assert(m_count == 0);
955
956 void used() noexcept
957 {
958 zth_assert(m_count < std::numeric_limits<size_t>::max());
959 m_count++;
961
962 bool unused() noexcept
963 {
964 zth_assert(m_count > 0);
965 if(--m_count > 0)
966 return false;
967
968 cleanup();
969 return true;
971
972 size_t refs() const noexcept
973 {
974 return m_count;
975 }
977protected:
978 virtual void cleanup() noexcept
979 {
980 delete this;
981 }
982
983private:
984 size_t m_count;
985};
987template <typename T, typename WhenTIsVoid>
988struct choose_type {
989 typedef T type;
990};
992template <typename WhenTIsVoid>
993struct choose_type<void, WhenTIsVoid> {
994 typedef WhenTIsVoid type;
995};
996
997# if __cplusplus >= 201103L
998template <size_t...>
999struct Sequence {};
1000# ifndef DOXYGEN
1001template <size_t N, size_t... S>
1002struct SequenceGenerator : SequenceGenerator<N - 1, N - 1, S...> {};
1003
1004template <size_t... S>
1005struct SequenceGenerator<0, S...> {
1006 typedef Sequence<S...> type;
1007};
1008# endif
1009# endif
1013# ifdef _DEBUG
1014template <typename T>
1016public:
1017 typedef safe_ptr type;
1018 typedef T pointer_type;
1020 // cppcheck-suppress noExplicitConstructor
1021 constexpr safe_ptr(pointer_type* p) noexcept
1022 : m_p(p)
1024
1025 constexpr operator pointer_type*() const noexcept
1026 {
1027 return ptr();
1029
1030 constexpr operator bool() const noexcept
1031 {
1032 return ptr();
1034
1035 constexpr14 pointer_type* operator->() const noexcept
1036 {
1037 zth_assert(ptr());
1038 return ptr();
1040
1041 constexpr14 pointer_type& operator*() const noexcept
1042 {
1043 zth_assert(ptr());
1044 // cppcheck-suppress nullPointerRedundantCheck
1045 return *ptr();
1046 }
1048protected:
1049 constexpr pointer_type* ptr() const noexcept
1050 {
1051 return m_p;
1052 }
1053
1054private:
1055 pointer_type* m_p;
1056};
1057# else // _DEBUG
1058
1059// No checking, use use the pointer.
1060template <typename T>
1061class safe_ptr {
1062public:
1063 typedef T* type;
1064};
1065# endif // !_DEBUG
1066
1089template <typename T>
1090class Singleton {
1091public:
1093 typedef T singleton_type;
1094
1095protected:
1100 constexpr14 Singleton() noexcept
1101 {
1102 // Do not enforce construction of only one Singleton, only register the first one
1103 // as 'the' instance.
1104
1105 if(!m_instance)
1106 m_instance = static_cast<singleton_type*>(this);
1107 }
1108
1113 ~Singleton()
1114 {
1115 if(m_instance == static_cast<singleton_type*>(this))
1116 m_instance = nullptr;
1117 }
1118
1119public:
1124 __attribute__((pure)) constexpr14 static typename safe_ptr<singleton_type>::type
1125 instance() noexcept
1126 {
1127 return m_instance;
1128 }
1129
1130private:
1132 static singleton_type* m_instance;
1133};
1134
1135template <typename T>
1136typename Singleton<T>::singleton_type* Singleton<T>::m_instance = nullptr;
1137
1147template <typename T>
1148class ThreadLocalSingleton {
1149public:
1151 typedef T singleton_type;
1152
1153protected:
1159 __attribute__((no_sanitize_undefined)) ThreadLocalSingleton()
1160 {
1161 // Do not enforce construction of only one Singleton, only register the first one
1162 // as 'the' instance.
1163
1164 if(!ZTH_TLS_GET(m_instance))
1165 ZTH_TLS_SET(m_instance, static_cast<singleton_type*>(this));
1166 }
1167
1172 __attribute__((no_sanitize_undefined)) ~ThreadLocalSingleton()
1173 {
1174 if(ZTH_TLS_GET(m_instance) == static_cast<singleton_type*>(this))
1175 ZTH_TLS_SET(m_instance, nullptr);
1176 }
1177
1178public:
1183 __attribute__((pure)) static typename safe_ptr<singleton_type>::type instance() noexcept
1184 {
1185 return ZTH_TLS_GET(m_instance);
1186 }
1187
1188private:
1190 ZTH_TLS_MEMBER(singleton_type*, m_instance)
1191};
1192
1193template <typename T>
1195 typename ThreadLocalSingleton<T>::singleton_type*, ThreadLocalSingleton<T>::m_instance,
1196 nullptr)
1197
1198
1214template <typename T, int8_t Prealloc = 1, typename Allocator = typename Config::Allocator<T>::type>
1216public:
1217 typedef T value_type;
1218 typedef Allocator allocator_type;
1219 typedef std::vector<value_type, allocator_type> vector_type;
1220
1221 enum {
1222 prealloc_request = Prealloc > 0 ? Prealloc : 0,
1223 prealloc_request_size = prealloc_request * sizeof(value_type),
1224 vector_size = sizeof(vector_type),
1225 buffer_size =
1226 prealloc_request_size > vector_size ? prealloc_request_size : vector_size,
1227 prealloc = buffer_size / sizeof(value_type),
1228 };
1229
1233 // cppcheck-suppress uninitMemberVar
1234 constexpr small_vector() noexcept
1235 : m_size()
1236 {
1237 static_assert(prealloc < std::numeric_limits<uint8_t>::max(), "");
1238 }
1239
1243 ~small_vector()
1244 {
1245 if(is_small())
1246 resize(0);
1247 else
1248 vector().~vector_type();
1249 }
1250
1254 value_type& operator[](size_t index) noexcept
1255 {
1256 zth_assert(index < size());
1257 return data()[index];
1258 }
1259
1263 value_type const& operator[](size_t index) const noexcept
1264 {
1265 zth_assert(index < size());
1266 return data()[index];
1267 }
1268
1274 value_type& front() noexcept
1275 {
1276 return (*this)[0];
1277 }
1278
1284 value_type const& front() const noexcept
1285 {
1286 return (*this)[0];
1287 }
1288
1294 value_type& back() noexcept
1295 {
1296 zth_assert(!empty());
1297 return (*this)[size() - 1U];
1298 }
1299
1305 value_type const& back() const noexcept
1306 {
1307 zth_assert(!empty());
1308 return (*this)[size() - 1U];
1309 }
1310
1314 value_type* data() noexcept
1315 {
1316 return is_small() ? array() : vector().data();
1317 }
1318
1322 value_type const* data() const noexcept
1323 {
1324 return is_small() ? array() : vector().data();
1325 }
1326
1330 bool empty() const noexcept
1331 {
1332 return is_small() ? m_size == 0 : vector().empty();
1333 }
1334
1338 size_t size() const noexcept
1339 {
1340 return is_small() ? m_size : vector().size();
1341 }
1342
1348 void reserve(size_t new_cap)
1349 {
1350 if(is_small() && new_cap <= prealloc)
1351 return;
1352
1353 if(is_small())
1354 make_vector(new_cap);
1355
1356 vector().reserve(new_cap);
1357 }
1358
1363 size_t capacity() const noexcept
1364 {
1365 return is_small() ? (size_t)prealloc : vector().capacity();
1366 }
1367
1373 void clear() noexcept
1374 {
1375 if(is_small())
1376 resize(0);
1377 else
1378 vector().clear();
1379 }
1380
1384 void clear_and_release() noexcept
1385 {
1386 if(is_small()) {
1387 resize(0);
1388 } else {
1389 vector_type v;
1390 v.swap(vector());
1391 }
1392 }
1393
1398 void push_back(value_type const& v)
1399 {
1400 reserve(size() + 1U);
1401
1402 if(m_size < prealloc) {
1403 new(&array()[m_size]) value_type(v);
1404 m_size++;
1405 } else
1406 vector().push_back(v);
1407 }
1408
1409# if __cplusplus >= 201103L
1414 template <class... Args>
1415 void emplace_back(Args&&... args)
1416 {
1417 reserve(size() + 1U);
1418
1419 if(m_size < prealloc) {
1420 new(&array()[m_size]) value_type(std::forward<Args>(args)...);
1421 m_size++;
1422 } else
1423 vector().emplace_back(std::forward<Args>(args)...);
1424 }
1425# endif
1426
1432 void pop_back() noexcept
1433 {
1434 zth_assert(!empty());
1435 if(is_small())
1436 array()[--m_size].~value_type();
1437 else
1438 vector().pop_back();
1439 }
1440
1446 void resize(size_t count, value_type const& x = value_type())
1447 {
1448 if(is_small()) {
1449 while(count < m_size)
1450 array()[--m_size].~value_type();
1451
1452 if(count <= prealloc) {
1453 while(count > m_size) {
1454 new(&array()[m_size]) value_type(x);
1455 m_size++;
1456 }
1457 } else {
1458 make_vector(count);
1459 vector().resize(count, x);
1460 }
1461 } else
1462 vector().resize(count, x);
1463 }
1464
1465protected:
1469 bool is_small() const noexcept
1470 {
1471 return m_size <= prealloc;
1472 }
1473
1477 value_type* array() noexcept
1478 {
1479 zth_assert(is_small());
1480 return reinterpret_cast<value_type*>(m_buffer);
1481 }
1482
1486 value_type const* array() const noexcept
1487 {
1488 zth_assert(is_small());
1489 return reinterpret_cast<value_type const*>(m_buffer);
1490 }
1491
1495 vector_type& vector() noexcept
1496 {
1497 zth_assert(!is_small());
1498 void* buffer = m_buffer;
1499 return *reinterpret_cast<vector_type*>(buffer);
1500 }
1501
1505 vector_type const& vector() const noexcept
1506 {
1507 zth_assert(!is_small());
1508 void const* buffer = m_buffer;
1509 return *reinterpret_cast<vector_type const*>(buffer);
1510 }
1511
1515 void make_vector(size_t new_cap)
1516 {
1517 if(!is_small())
1518 return;
1519
1520 vector_type v;
1521 v.reserve(std::max(new_cap, (size_t)m_size));
1522
1523 for(size_t i = 0; i < m_size; i++) {
1524# if __cplusplus >= 201103L
1525 v.emplace_back(std::move(array()[i]));
1526# else
1527 v.push_back(array()[i]);
1528# endif
1529 array()[i].~value_type();
1530 }
1531
1532 m_size = prealloc + 1U;
1533 new(m_buffer) vector_type;
1534 vector().swap(v);
1535 }
1536
1537private:
1542 alignas(vector_type) alignas(value_type) char m_buffer[buffer_size];
1543
1551 uint8_t m_size;
1552};
1554template <size_t size>
1555struct smallest_uint_size {};
1556
1557template <
1558 uint64_t x, typename size = smallest_uint_size<
1559 x >= 0x100000000ULL ? 8U
1560 : x >= 0x10000U ? 4U
1561 : x >= 0x100U ? 2U
1562 : 1U> >
1563struct smallest_uint {
1564 typedef uint64_t type;
1565};
1567template <uint64_t x>
1568struct smallest_uint<x, smallest_uint_size<1> > {
1569 typedef uint8_t type;
1570};
1572template <uint64_t x>
1573struct smallest_uint<x, smallest_uint_size<2> > {
1574 typedef uint16_t type;
1575};
1577template <uint64_t x>
1578struct smallest_uint<x, smallest_uint_size<4> > {
1579 typedef uint32_t type;
1580};
1581
1582
1583# if __cplusplus >= 201703L
1584// Based on https://gist.github.com/utilForever/1a058050b8af3ef46b58bcfa01d5375d
1585namespace impl {
1586template <class T, class... TArgs>
1587decltype(void(T{std::declval<TArgs>()...}), std::true_type{}) test_is_braces_constructible(int);
1589template <class, class...>
1590std::false_type test_is_braces_constructible(...);
1592template <class T, class... TArgs>
1594
1595struct any_type {
1596 template <class T>
1597 // cppcheck-suppress noExplicitConstructor
1598 constexpr operator T();
1599};
1600} // namespace impl
1602template <class T>
1603auto to_tuple(T&& object) noexcept
1604{
1605 using type = std::decay_t<T>;
1606
1607 // Repeat as required...
1608 if constexpr(impl::is_braces_constructible<
1610 impl::any_type>{}) {
1611 auto&& [p1, p2, p3, p4] = std::forward<T>(object);
1612 return std::make_tuple(p1, p2, p3, p4);
1613 } else if constexpr(impl::is_braces_constructible<
1615 auto&& [p1, p2, p3] = std::forward<T>(object);
1616 return std::make_tuple(p1, p2, p3);
1618 auto&& [p1, p2] = std::forward<T>(object);
1619 return std::make_tuple(p1, p2);
1621 auto&& [p1] = std::forward<T>(object);
1622 return std::make_tuple(p1);
1623 } else {
1624 return std::make_tuple();
1625 }
1627
1628# define ZTH_STRUCTURED_BINDING_FORWARDING(Class) \
1629 template <typename T> \
1630 struct std::tuple_size<Class<T>> : std::tuple_size<T> {}; \
1631 \
1632 template <size_t N, typename T> \
1633 struct std::tuple_element<N, Class<T>> : std::tuple_element<N, T> {}; \
1634 \
1635 template <size_t N, typename T> \
1636 decltype(auto) get(Class<T>& f) noexcept \
1637 { \
1638 auto& x = to_tuple(*f); \
1639 return get<N>(x); \
1640 }
1641# define ZTH_STRUCTURED_BINDING_FORWARDING_NS(Class) \
1642 } /* assume namespace zth */ \
1643 ZTH_STRUCTURED_BINDING_FORWARDING(zth::Class) \
1644 namespace zth {
1645# else
1646# define ZTH_STRUCTURED_BINDING_FORWARDING(Class)
1647# define ZTH_STRUCTURED_BINDING_FORWARDING_NS(Class)
1648# endif // C++17
1649
1650
1651} // namespace zth
1652
1653#endif // __cplusplus
1654
1660#ifdef __cplusplus
1661EXTERN_C ZTH_EXPORT ZTH_INLINE void zth_banner()
1662{
1663 zth::banner();
1664}
1665#else
1666ZTH_EXPORT void zth_banner();
1667#endif
1668
1674ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2), noreturn)) void
1675zth_abort(char const* fmt, ...);
1676
1682#ifdef __cplusplus
1683EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) void
1684zth_log_color(int color, char const* fmt, ...)
1685{
1686 va_list args;
1687 va_start(args, fmt);
1688 zth::log_colorv(color, fmt, args);
1689 va_end(args);
1690}
1691#else
1692ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 2, 3))) void
1693zth_log_color(int color, char const* fmt, ...);
1694#endif
1695
1701#ifdef __cplusplus
1702EXTERN_C ZTH_EXPORT ZTH_INLINE __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void
1703zth_log(char const* fmt, ...)
1704{
1705 va_list args;
1706 va_start(args, fmt);
1707 zth::logv(fmt, args);
1708 va_end(args);
1709}
1710#else
1711ZTH_EXPORT __attribute__((format(ZTH_ATTR_PRINTF, 1, 2))) void zth_log(char const* fmt, ...);
1712#endif
1713
1714#ifdef ZTH_OS_BAREMETAL
1715// newlib probably doesn't have these. Provide some default implementation for
1716// them.
1717EXTERN_C __attribute__((format(gnu_printf, 2, 0))) int asprintf(char** strp, const char* fmt, ...);
1718EXTERN_C __attribute__((format(gnu_printf, 2, 0))) int
1719vasprintf(char** strp, const char* fmt, va_list ap);
1720#endif
1721
1722#endif // ZTH_UTIL_H
Singleton pattern.
Definition util.h:1088
T singleton_type
Alias of the T template parameter.
Definition util.h:1091
Singleton pattern, but only per-thread.
Definition util.h:1146
T singleton_type
Alias of the T template parameter.
Definition util.h:1149
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:860
Copy-on-write string.
Definition util.h:324
char operator[](size_t pos) const
Definition util.h:411
string str() &&
Definition util.h:384
bool isConst() const noexcept
Definition util.h:447
string const & local() const
Definition util.h:458
char const & at(size_t pos) const
Definition util.h:406
char const * c_str() const
Definition util.h:396
size_t length() const noexcept
Definition util.h:436
cow_string & operator=(cow_string const &s)
Definition util.h:346
size_t size() const noexcept
Definition util.h:431
char const * data() const
Definition util.h:421
void clear() noexcept
Definition util.h:441
bool empty() const noexcept
Definition util.h:426
bool isLocal() const noexcept
Definition util.h:452
Wrapper for a pointer, which checks validity of the pointer upon dereference.
Definition util.h:1013
A simple std::vector, which can contain Prealloc without heap allocation.
Definition util.h:1213
Allocator allocator_type
Definition util.h:1216
std::vector< value_type, allocator_type > vector_type
Definition util.h:1217
void zth_banner()
Prints a banner line with version and configuration information.
Definition util.h:1659
void zth_log(char const *fmt,...)
Logs a given printf()-like formatted string.
Definition util.h:1701
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:337
void zth_log_color(int color, char const *fmt,...)
Logs a given printf()-like formatted string using an ANSI color code.
Definition util.h:1682
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:236
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:292
#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.
decltype(test_is_braces_constructible< T, TArgs... >(0)) is_braces_constructible
Definition util.h:1591
decltype(void(T{std::declval< TArgs >()...}), std::true_type{}) test_is_braces_constructible(int)
auto to_tuple(T &&object) noexcept
Definition util.h:1601
cow_string str(T value)
Returns an zth::string representation of the given value.
Definition util.h:497
string formatv(char const *fmt, va_list args)
Format like vsprintf(), but save the result in an zth::string.
Definition util.cpp:254
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition util.h:483
static bool const UseLimitedFormatSpecifiers
Use limited formatting specifiers.
Definition config.h:200
Change the name of a fiber returned by zth_async.
Definition async.h:201
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:217
#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