• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aremmell / libsir / 849

24 Sep 2023 02:56AM UTC coverage: 94.391% (+0.1%) from 94.296%
849

push

gitlab-ci

aremmell
clean up/improve test rig

29 of 29 new or added lines in 1 file covered. (100.0%)

3652 of 3869 relevant lines covered (94.39%)

523332.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.04
/include/sir.hh
1
/*
2
 * @file sir.hh
3
 * @brief C++ interface to libsir
4
 *
5
 * @author    Ryan M. Lederman \<lederman@gmail.com\>
6
 * @date      2018-2023
7
 * @version   2.2.4
8
 * @copyright The MIT License (MIT)
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
11
 * this software and associated documentation files (the "Software"), to deal in
12
 * the Software without restriction, including without limitation the rights to
13
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
14
 * the Software, and to permit persons to whom the Software is furnished to do so,
15
 * subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included in all
18
 * copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
22
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
23
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
 */
27
#ifndef _SIR_HH_INCLUDED
28
# define _SIR_HH_INCLUDED
29

30
# include "sir.h"
31
# include "sir/helpers.h"
32
# include "sir/internal.h"
33
# include <type_traits>
34
# include <stdexcept>
35
# include <algorithm>
36
# include <exception>
37
# include <iostream>
38
# include <memory>
39
# include <tuple>
40
# include <string>
41
# include <array>
42

43
# if HAS_INCLUDE(<format>) && !defined (SIR_NO_STD_FORMAT) && \
44
     !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
45
#  include <format>
46
#  define __SIR_HAVE_STD_FORMAT__
47
# endif
48
# if HAS_INCLUDE(<boost/format.hpp>) && !defined(SIR_NO_BOOST_FORMAT)
49
#  include <boost/format.hpp>
50
#  define __SIR_HAVE_BOOST_FORMAT__
51
# endif
52
# if HAS_INCLUDE(<fmt/format.h>) && !defined(SIR_NO_FMT_FORMAT)
53
#  define FMT_HEADER_ONLY
54
#  include <fmt/format.h>
55
#  define __SIR_HAVE_FMT_FORMAT__
56
# endif
57
# if !defined(SIR_NO_STD_IOSTREAM)
58
#  include <iostream>
59
# endif
60

61
/**
62
 * @addtogroup public
63
 * @{
64
 *
65
 * @addtogroup cpp C++ Wrapper
66
 * @{
67
 */
68

69
/** libsir C++ wrapper implementation. */
70
namespace sir
71
{
72
    /**
73
     * @struct error
74
     * @brief A libsir error object, containing a numeric error code and associated
75
     * message.
76
     */
77
    struct error {
78
        uint16_t code {};    /**< libsir error code (see ::sir_errorcode). */
79
        std::string message; /**< Message describing the error that occurred. */
80
    };
81

82
    /**
83
     * @struct error_info
84
     * @brief Extends ::error to provide granular information about an error.
85
     *
86
     * In order to enable custom error handling, provides all available infor-
87
     * mation about an error, without applying the default message formatting.
88
     */
89
    struct error_info : public error {
90
        std::string func;       /**< Name of the function in which the error occurred. */
91
        std::string file;       /**< Name of the file in which the error occurred. */
92
        uint32_t line {};       /**< Line number at which the error occurred. */
93
        int os_code {};         /**< If an OS/libc error, the associated code. */
94
        std::string os_message; /**< If an OS/libc error, the associated message. */
95
    };
96

97
    /**
98
     * @class exception
99
     * @brief Derives from std::runtime_error in order to be caught easily by
100
     * standard try/catch blocks, as well as to provide useful error messages
101
     * by way of the what() method.
102
     */
103
    class exception : public std::runtime_error {
104
    public:
105
        using std::runtime_error::runtime_error;
106

107
        exception() = delete;
108
        explicit exception(const error& err) : exception(err.message) { }
5✔
109
        ~exception() override = default;
12✔
110

111
        static exception from_libsir_error() {
4✔
112
            std::array<char, SIR_MAXERROR> msg {};
4✔
113
            const auto code = sir_geterror(msg.data());
4✔
114
            return exception({ code, msg.data() });
8✔
115
        }
12✔
116
    };
117

118
    /**
119
     * @class policy
120
     * @brief Defines the partially abstract interface for a policy which
121
     * controls the behavior of logger at runtime.
122
     */
123
    class policy {
124
    public:
125
        policy() = default;
126
        virtual ~policy() = default;
20✔
127

128
        /**
129
         * Called by logger before initializing libsir. Provides the policy with
130
         * an opportunity to customize the initial libsir configuration.
131
         *
132
         * @param data initial libisr configuration data.
133
         *
134
         * @see ::sirinit
135
         * @see ::sir_makeinit
136
         *
137
         * @note Most, if not all of the options chosen during initialization
138
         * can be modified at runtime using functions provided by libsir.
139
         *
140
         * @note For the default configuration, pass the address of the ::sirinit
141
         * structure to ::sir_makeinit.
142
         *
143
         * @returns true if data was successfully configured, or false if an
144
         * error occurred. Returning false will cause logger to abort the
145
         * initialization process–either by throwing an exception, or by entering
146
         * an 'invalid' state (determined by policy::throw_on_error).
147
         */
148
        virtual bool get_init_data(sirinit& data) const noexcept = 0;
149

150
        /**
151
         * Called by logger immediately after libsir has been initialized. This
152
         * is the moment in time which should be utilized for further configuration
153
         * than is possible via ::sirinit alone.
154
         *
155
         * Some items include:
156
         * - Setting the ::sir_colormode for stdio (::sir_setcolormode)
157
         * - Setting ::sir_textstyle on a per-level basis for stdio (::sir_settextstyle)
158
         * - Adding log files (::sir_addfile)
159
         * - Loading plugins (::sir_loadplugin)
160
         *
161
         * @returns true if configuration was completed successfully, or false if
162
         * an error occurred. Returning false will cause logger to abort the
163
         * initialization process–either by throwing an exception, or by entering
164
         * an 'invalid' state (determined by policy::throw_on_error).
165
         */
166
        virtual bool on_init_complete() const noexcept = 0;
167

168
        /**
169
         * Determines whether or not exceptions are thrown in the event that a
170
         * libsir or OS-level error is encountered by logger or its associated
171
         * adapter(s).
172
         *
173
         * @note If exceptions are not enabled, the caller of a method that could
174
         * potentially fail is responsible for obtaining error information by
175
         * calling logger::get_error and reacting accordingly.
176
         *
177
         * @returns true if logger should throw exceptions (derived from std::
178
         * exception) when errors are encountered, or false if errors should
179
         * simply be communicated through a pass/fail Boolean return value.
180
         */
181
        static constexpr bool throw_on_error() noexcept {
182
            return false;
183
        }
184

185
        /**
186
         * Useful when complications arise; the question "which policy caused that
187
         * to happen?!" is then easy to answer.
188
         *
189
         * @returns A unique name for the policy.
190
         */
191
        virtual constexpr const char* get_name() const = 0;
192
    };
193

194
    /** Ensures that T derives from policy. */
195
    template<typename T>
196
    concept DerivedFromPolicy = std::is_base_of_v<policy, T>;
197

198
    /**
199
     * @class default_policy
200
     * @brief In the event that no custom configuration or behavior is desired,
201
     * provides defaults for everything.
202
     *
203
     * - Uses all default values for sir_init
204
     * - Performs no post-initialization configuration
205
     * - Exceptions are thrown when errors are encountered
206
     */
207
    class default_policy : public policy {
208
    public:
209
        default_policy() = default;
210
        ~default_policy() override = default;
20✔
211

212
        bool get_init_data(sirinit& data) const noexcept final {
10✔
213
            return sir_makeinit(&data);
10✔
214
        }
215

216
        constexpr bool on_init_complete() const noexcept final {
9✔
217
            return true;
9✔
218
        }
219

220
        static constexpr bool throw_on_error() noexcept {
221
            return true;
222
        }
223

224
        constexpr const char* get_name() const final {
×
225
            return "Default";
×
226
        }
227
    };
228

229
    /**
230
     * @class adapter
231
     * @brief Defines the abstract interface for an adapter, which ultimately
232
     * becomes a public base class of logger (there can be more than one).
233
     *
234
     * adapter is designed to provide flexibility and extensibility in relation
235
     * to the public interface that is implemented by a logger.
236
     *
237
     * @note One must take care to ensure that the methods implemented by an
238
     * adapter can coexist with any other adapters that are applied to the logger
239
     * template.
240
     *
241
     * @see ::std_format_adapter
242
     * @see ::boost_format_adapter
243
     * @see ::fmt_format_adapter
244
     * @see ::std_iostream_adapter
245
     */
246
    class adapter {
247
    protected:
248
        adapter() = default;
14✔
249
        virtual ~adapter() = default;
28✔
250
    };
251

252
    /**
253
     * @brief Handles a potential error; if an error is present and the policy
254
     * in place requires throwing an exception, retrieves the associated error
255
     * code and message from libsir, then throws.
256
     *
257
     * @param TPolicy policy A derived class of policy which determines whether
258
     * or not to throw exceptions upon encountering an error.
259
     *
260
     * @param expr bool An expression that is evaluated against false. If false,
261
     *  an error is determined to have occurred and an exception will be thrown
262
     * if the policy requires it.
263
     *
264
     * @returns bool If no exception was thrown, the return value is expr (true
265
     * if no error occurred, false otherwise).
266
     */
267
    template<DerivedFromPolicy TPolicy>
268
    static inline bool throw_on_policy(bool expr) noexcept(false) {
77✔
269
        if (!expr) {
77✔
270
            SIR_ASSERT(expr);
4✔
271
            if constexpr(TPolicy::throw_on_error()) {
272
                throw exception::from_libsir_error();
4✔
273
            }
274
        }
275
        return expr;
73✔
276
    }
277

278
    /**
279
     * @class default_adapter
280
     * @brief The default adapter implementation.
281
     *
282
     * Utilizes the same code path that the C interface itself does, in order to
283
     * achieve maximum performance (i.e., no unnecessary bloat is added).
284
     *
285
     * @param TPolicy policy A derived class of policy which controls the behavior
286
     * of logger and by association, its adapters.
287
     */
288
    template<DerivedFromPolicy TPolicy>
289
    class default_adapter : public adapter {
290
    public:
291
        default_adapter() = default;
10✔
292
        ~default_adapter() override = default;
20✔
293

294
        /** Logs a debug message (see ::sir_debug). */
295
        PRINTF_FORMAT_ATTR(2, 3)
296
        bool debug(PRINTF_FORMAT const char* format, ...) const {
4✔
297
            _SIR_L_START(format);
4✔
298
            ret = _sir_logv(SIRL_DEBUG, format, args);
4✔
299
            _SIR_L_END();
4✔
300
            return throw_on_policy<TPolicy>(ret);
4✔
301
        }
302

303
        /** Logs an informational message (see ::sir_info). */
304
        PRINTF_FORMAT_ATTR(2, 3)
305
        bool info(PRINTF_FORMAT const char* format, ...) const {
2✔
306
            _SIR_L_START(format);
2✔
307
            ret = _sir_logv(SIRL_INFO, format, args);
2✔
308
            _SIR_L_END();
2✔
309
            return throw_on_policy<TPolicy>(ret);
2✔
310
        }
311

312
        /** Logs a notice message (see ::sir_notice). */
313
        PRINTF_FORMAT_ATTR(2, 3)
314
        bool notice(PRINTF_FORMAT const char* format, ...) const {
2✔
315
            _SIR_L_START(format);
2✔
316
            ret = _sir_logv(SIRL_NOTICE, format, args);
2✔
317
            _SIR_L_END();
2✔
318
            return throw_on_policy<TPolicy>(ret);
2✔
319
        }
320

321
        /** Logs a warning message (see ::sir_warn). */
322
        PRINTF_FORMAT_ATTR(2, 3)
323
        bool warn(PRINTF_FORMAT const char* format, ...) const {
2✔
324
            _SIR_L_START(format);
2✔
325
            ret = _sir_logv(SIRL_WARN, format, args);
2✔
326
            _SIR_L_END();
2✔
327
            return throw_on_policy<TPolicy>(ret);
2✔
328
        }
329

330
        /** Logs an error message (see ::sir_error). */
331
        PRINTF_FORMAT_ATTR(2, 3)
332
        bool error(PRINTF_FORMAT const char* format, ...) const {
2✔
333
            _SIR_L_START(format);
2✔
334
            ret = _sir_logv(SIRL_ERROR, format, args);
2✔
335
            _SIR_L_END();
2✔
336
            return throw_on_policy<TPolicy>(ret);
2✔
337
        }
338

339
        /** Logs a critical message (see ::sir_crit). */
340
        PRINTF_FORMAT_ATTR(2, 3)
341
        bool crit(PRINTF_FORMAT const char* format, ...) const {
2✔
342
            _SIR_L_START(format);
2✔
343
            ret = _sir_logv(SIRL_CRIT, format, args);
2✔
344
            _SIR_L_END();
2✔
345
            return throw_on_policy<TPolicy>(ret);
2✔
346
        }
347

348
        /** Logs an alert message (see ::sir_alert). */
349
        PRINTF_FORMAT_ATTR(2, 3)
350
        bool alert(PRINTF_FORMAT const char* format, ...) const {
2✔
351
            _SIR_L_START(format);
2✔
352
            ret = _sir_logv(SIRL_ALERT, format, args);
2✔
353
            _SIR_L_END();
2✔
354
            return throw_on_policy<TPolicy>(ret);
2✔
355
        }
356

357
        /** Logs an emergency message (see ::sir_emerg). */
358
        PRINTF_FORMAT_ATTR(2, 3)
359
        bool emerg(PRINTF_FORMAT const char* format, ...) const {
2✔
360
            _SIR_L_START(format);
2✔
361
            ret = _sir_logv(SIRL_EMERG, format, args);
2✔
362
            _SIR_L_END();
2✔
363
            return throw_on_policy<TPolicy>(ret);
2✔
364
        }
365
    };
366

367
# if defined(__SIR_HAVE_STD_FORMAT__)
368
    /**
369
     * @class std_format_adapter
370
     * @brief Adapter for std::format (when available).
371
     *
372
     * TODO: update description
373
     *
374
     * @param TPolicy policy A derived class of policy which controls the behavior
375
     * of logger and by association, its adapters.
376
     */
377
    template<DerivedFromPolicy TPolicy>
378
    class std_format_adapter : public adapter {
379
    public:
380
        std_format_adapter() = default;
1✔
381
        ~std_format_adapter() override = default;
2✔
382

383
        template<typename... Args>
384
        inline bool debug_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
385
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
386
            const bool ret = sir_debug("%s", str.c_str());
1✔
387
            return throw_on_policy<TPolicy>(ret);
2✔
388
        }
1✔
389

390
        template<typename... Args>
391
        inline bool info_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
392
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
393
            const bool ret = sir_info("%s", str.c_str());
1✔
394
            return throw_on_policy<TPolicy>(ret);
2✔
395
        }
1✔
396

397
        template<typename... Args>
398
        inline bool notice_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
399
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
400
            const bool ret = sir_notice("%s", str.c_str());
1✔
401
            return throw_on_policy<TPolicy>(ret);
2✔
402
        }
1✔
403

404
        template<typename... Args>
405
        inline bool warn_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
406
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
407
            const bool ret = sir_warn("%s", str.c_str());
1✔
408
            return throw_on_policy<TPolicy>(ret);
2✔
409
        }
1✔
410

411
        template<typename... Args>
412
        inline bool error_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
413
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
414
            const bool ret = sir_error("%s", str.c_str());
1✔
415
            return throw_on_policy<TPolicy>(ret);
2✔
416
        }
1✔
417

418
        template<typename... Args>
419
        inline bool crit_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
420
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
421
            const bool ret = sir_crit("%s", str.c_str());
1✔
422
            return throw_on_policy<TPolicy>(ret);
2✔
423
        }
1✔
424

425
        template<typename... Args>
426
        inline bool alert_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
427
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
428
            const bool ret = sir_alert("%s", str.c_str());
1✔
429
            return throw_on_policy<TPolicy>(ret);
2✔
430
        }
1✔
431

432
        template<typename... Args>
433
        inline bool emerg_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
434
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
435
            const bool ret = sir_emerg("%s", str.c_str());
1✔
436
            return throw_on_policy<TPolicy>(ret);
2✔
437
        }
1✔
438
    };
439
# endif // !__SIR_HAVE_STD_FORMAT__
440

441
# if defined(__SIR_HAVE_BOOST_FORMAT__)
442
    /**
443
     * @class boost_format_adapter
444
     * @brief Adapter for boost::format (when available).
445
     *
446
     * TODO: update description
447
     *
448
     * @param TPolicy policy A derived class of policy which controls the behavior
449
     * of logger and by association, its adapters.
450
     */
451
    template<DerivedFromPolicy TPolicy>
452
    class boost_format_adapter : public adapter {
453
    public:
454
        boost_format_adapter() = default;
1✔
455
        ~boost_format_adapter() override = default;
2✔
456

457
        bool debug_boost(const boost::format& fmt) const {
1✔
458
            const bool ret = sir_debug("%s", fmt.str().c_str());
1✔
459
            return throw_on_policy<TPolicy>(ret);
1✔
460
        }
461

462
        bool info_boost(const boost::format& fmt) const {
1✔
463
            const bool ret = sir_info("%s", fmt.str().c_str());
1✔
464
            return throw_on_policy<TPolicy>(ret);
1✔
465
        }
466

467
        bool notice_boost(const boost::format& fmt) const {
1✔
468
            const bool ret = sir_notice("%s", fmt.str().c_str());
1✔
469
            return throw_on_policy<TPolicy>(ret);
1✔
470
        }
471

472
        bool warn_boost(const boost::format& fmt) const {
1✔
473
            const bool ret = sir_warn("%s", fmt.str().c_str());
1✔
474
            return throw_on_policy<TPolicy>(ret);
1✔
475
        }
476

477
        bool error_boost(const boost::format& fmt) const {
1✔
478
            const bool ret = sir_error("%s", fmt.str().c_str());
1✔
479
            return throw_on_policy<TPolicy>(ret);
1✔
480
        }
481

482
        bool crit_boost(const boost::format& fmt) const {
1✔
483
            const bool ret = sir_crit("%s", fmt.str().c_str());
1✔
484
            return throw_on_policy<TPolicy>(ret);
1✔
485
        }
486

487
        bool alert_boost(const boost::format& fmt) const {
1✔
488
            const bool ret = sir_alert("%s", fmt.str().c_str());
1✔
489
            return throw_on_policy<TPolicy>(ret);
1✔
490
        }
491

492
        bool emerg_boost(const boost::format& fmt) const {
1✔
493
            const bool ret = sir_emerg("%s", fmt.str().c_str());
1✔
494
            return throw_on_policy<TPolicy>(ret);
1✔
495
        }
496
    };
497
# endif // !__SIR_HAVE_BOOST_FORMAT__
498

499
# if defined(__SIR_HAVE_FMT_FORMAT__)
500
    /**
501
     * @class fmt_format_adapter
502
     * @brief Adapter for fmt (when available).
503
     *
504
     * TODO: update description
505
     *
506
     * @param TPolicy policy A derived class of policy which controls the behavior
507
     * of logger and by association, its adapters.
508
     */
509
    template<DerivedFromPolicy TPolicy>
510
    class fmt_format_adapter : public adapter {
511
    public:
512
        fmt_format_adapter() = default;
1✔
513
        ~fmt_format_adapter() override = default;
2✔
514

515
        template<typename... Args>
516
        inline bool debug_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
517
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
518
            const auto ret = sir_debug("%s", str.c_str());
1✔
519
            return throw_on_policy<TPolicy>(ret);
2✔
520
        }
1✔
521

522
        template<typename... Args>
523
        inline bool info_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
524
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
525
            const auto ret = sir_info("%s", str.c_str());
1✔
526
            return throw_on_policy<TPolicy>(ret);
2✔
527
        }
1✔
528

529
        template<typename... Args>
530
        inline bool notice_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
531
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
532
            const auto ret = sir_notice("%s", str.c_str());
1✔
533
            return throw_on_policy<TPolicy>(ret);
2✔
534
        }
1✔
535

536
        template<typename... Args>
537
        inline bool warn_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
538
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
539
            const auto ret = sir_warn("%s", str.c_str());
1✔
540
            return throw_on_policy<TPolicy>(ret);
2✔
541
        }
1✔
542

543
        template<typename... Args>
544
        inline bool error_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
545
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
546
            const auto ret = sir_error("%s", str.c_str());
1✔
547
            return throw_on_policy<TPolicy>(ret);
2✔
548
        }
1✔
549

550
        template<typename... Args>
551
        inline bool crit_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
552
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
553
            const auto ret = sir_crit("%s", str.c_str());
1✔
554
            return throw_on_policy<TPolicy>(ret);
2✔
555
        }
1✔
556

557
        template<typename... Args>
558
        inline bool alert_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
559
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
560
            const auto ret = sir_alert("%s", str.c_str());
1✔
561
            return throw_on_policy<TPolicy>(ret);
2✔
562
        }
1✔
563

564
        template<typename... Args>
565
        inline bool emerg_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
566
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
567
            const auto ret = sir_emerg("%s", str.c_str());
1✔
568
            return throw_on_policy<TPolicy>(ret);
2✔
569
        }
1✔
570
    };
571
# endif // !__SIR_HAVE_FMT_FORMAT__
572

573
# if !defined(SIR_NO_STD_IOSTREAM)
574
    /** Ensures that T derives from std::streambuf. */
575
    template<typename T>
576
    concept DerivedFromStreamBuf = std::is_base_of_v<std::streambuf, T>;
577

578
    /**
579
     * @class std_iostream_adapter
580
     * @brief Provides a std::iostream interface to libsir's logging functions.
581
     *
582
     * Implements a public std::ostream member for each available logging level
583
     * (e.g. debug_stream, info_stream, ..., emerg_stream).
584
     *
585
     * @note Use std::endl or std::flush to indicate the end of a log message if
586
     * using the iostream << operator.
587
     *
588
     * @param TPolicy policy A derived class of policy which controls the behavior
589
     * of logger and by association, its adapters.
590
     */
591
    template<DerivedFromPolicy TPolicy>
592
    class std_iostream_adapter : public adapter {
593
    public:
594
        std_iostream_adapter() = default;
1✔
595
        ~std_iostream_adapter() override = default;
2✔
596

597
        template<typename TFunc>
598
        class buffer : public std::streambuf {
599
        public:
600
            using array_type = std::array<char_type, SIR_MAXMESSAGE>;
601

602
            buffer() = delete;
603

604
            explicit buffer(TFunc pfn) : _pfn(pfn) {
8✔
605
                [[maybe_unused]]
606
                bool ok = throw_on_policy<TPolicy>(pfn != nullptr && _arr != nullptr);
8✔
607
                reset();
8✔
608
            }
8✔
609

610
            ~buffer() override = default;
32✔
611

612
        protected:
613
            void reset() {
23✔
614
                if (_arr) {
23✔
615
                    _arr->fill('\0');
23✔
616
                    setp(_arr->data(), _arr->data() + _arr->size() - 1);
92✔
617
                }
618
            }
23✔
619

620
            bool write_out() {
15✔
621
                for (auto it = _arr->rbegin(); it != _arr->rend(); it++) {
61,076✔
622
                    if ((*it != '\0') && (*it == '\n')) {
61,072✔
623
                        *it = '\0';
11✔
624
                        break;
11✔
625
                    }
626
                }
627

628
                const bool write = _pfn ? _pfn(_arr->data()) : false;
30✔
629
                reset();
15✔
630
                return throw_on_policy<TPolicy>(write);
15✔
631
            }
632

633
            int_type overflow(int_type ch) override {
1✔
634
                if (ch != traits_type::eof() && write_out()) {
1✔
635
                    return ch;
1✔
636
                }
637
                return traits_type::eof();
×
638
            }
639

640
            int sync() override {
14✔
641
                return write_out() ? 0 : -1;
14✔
642
            }
643

644
            std::streamsize xsputn(const char_type* s, std::streamsize count) override {
21✔
645
                std::streamsize written = 0;
21✔
646
                do {
647
                    ptrdiff_t left = epptr() - pptr();
23✔
648
                    if (!left) {
23✔
649
                        int_type ch = overflow(traits_type::to_int_type(*(s + written)));
1✔
650
                        if (ch != traits_type::eof()) {
1✔
651
                            continue;
1✔
652
                        } else {
653
                            break;
×
654
                        }
655
                    }
656

657
                    auto this_write = std::min(static_cast<std::streamsize>(left),
44✔
658
                        count - written);
22✔
659
                    memcpy(pptr(), s + written, static_cast<size_t>(this_write));
22✔
660
                    pbump(static_cast<int>(this_write));
22✔
661
                    written += this_write;
22✔
662
                } while (written < count);
23✔
663

664
                return written;
21✔
665
            }
666

667
        private:
668
            std::shared_ptr<array_type> _arr = std::make_shared<array_type>();
669
            TFunc _pfn = nullptr;
670
        };
671

672
        template<DerivedFromStreamBuf TBuffer>
673
        class stream : public std::ostream {
674
        public:
675
            explicit stream(const TBuffer& buf) : std::ostream(&_buf), _buf(buf) {}
8✔
676
            ~stream() override = default;
16✔
677

678
        private:
679
            TBuffer _buf;
680
        };
681

682
        using log_func = bool(*)(const char*, ...);
683
        using buffer_type = buffer<log_func>;
684
        using stream_type = stream<buffer_type>;
685

686
        stream_type debug_stream  {buffer_type {&sir_debug}};
687
        stream_type info_stream   {buffer_type {&sir_info}};
688
        stream_type notice_stream {buffer_type {&sir_notice}};
689
        stream_type warn_stream   {buffer_type {&sir_warn}};
690
        stream_type error_stream  {buffer_type {&sir_error}};
691
        stream_type crit_stream   {buffer_type {&sir_crit}};
692
        stream_type alert_stream  {buffer_type {&sir_alert}};
693
        stream_type emerg_stream  {buffer_type {&sir_emerg}};
694
    };
695
# endif // SIR_NO_STD_IOSTREAM
696

697
    /** Utility template for obtaining the type of Nth item in a parameter pack. */
698
    template<size_t N, typename ...Ts>
699
    using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type;
700

701
    /** Ensures (at compile time) that all Ts are derived classes of TBase. */
702
    template<size_t N, typename TBase, typename ...Ts>
703
    consteval auto all_derived_from_t() {
704
        if constexpr(!std::is_base_of_v<TBase, NthTypeOf<N, Ts...>>)
705
            return std::false_type::value;
706
        if constexpr(N == 0)
707
            return std::true_type::value;
708
        else
709
            return all_derived_from_t<N - 1, TBase, Ts...>();
710
    }
711

712
    /** True if all Ts are derived classes of TBase, otherwise false. */
713
    template<class TBase, class ...Ts>
714
    concept DerivedFromT = all_derived_from_t<sizeof...(Ts) - 1, TBase, Ts...>();
715

716
    /**
717
     * @class logger
718
     * @brief The primary C++ interface to libsir.
719
     *
720
     * Instantiate this class in order to access libsir with all the benefits
721
     * of C++, including RAII initialization/cleanup, custom adapters, and more.
722
     *
723
     * @param RAII      Set to `true` to enable 'resource acquisition is
724
     *                  initialization' behavior (i.e., libsir is initialized by
725
     *                  the ctor, and cleaned up by the dtor). Set to `false` for
726
     *                  manual management of initialization/cleanup.
727
     * @param TPolicy   A policy class which will be responsible for certain
728
     *                  aspects of logger's behavior.
729
     * @param TAdapters One or more adapter classes whose public methods will be
730
     *                  exposed by this class.
731
     */
732
    template<bool RAII, DerivedFromPolicy TPolicy, template<class> class ...TAdapters>
733
    requires DerivedFromT<adapter, TAdapters<TPolicy>...>
734
    class logger : public TAdapters<TPolicy>... {
735
    public:
736
        logger() : TAdapters<TPolicy>()... {
20✔
737
            if constexpr(RAII) {
738
                [[maybe_unused]]
739
                bool ok = throw_on_policy<TPolicy>(_init());
18✔
740
            }
741
        }
20✔
742

743
        logger(const logger&) = delete;
744
        logger(const logger&&) = delete;
745

746
        ~logger() override {
18✔
747
            if constexpr(RAII) {
748
                if (!cleanup()) {
16✔
749
                    SIR_ASSERT(false);
×
750
                }
751
            }
752
        }
34✔
753

754
        logger& operator=(const logger&) = delete;
755
        logger& operator=(const logger&&) = delete;
756

757
        bool init() const noexcept {
2✔
758
            return is_initialized() ? false: _init();
2✔
759
        }
760

761
        bool cleanup() const noexcept {
20✔
762
            return is_initialized() ? sir_cleanup() : false;
20✔
763
        }
764

765
        bool is_initialized() const noexcept {
24✔
766
            return sir_isinitialized();
24✔
767
        }
768

769
        error get_error() const {
2✔
770
            std::array<char, SIR_MAXERROR> message {};
2✔
771
            const auto code = sir_geterror(message.data());
2✔
772
            return { code, message.data() };
4✔
773
        }
6✔
774

775
        error_info get_error_info() const {
2✔
776
            sir_errinfo errinfo {};
2✔
777
            sir_geterrorinfo(&errinfo);
2✔
778
            return { { errinfo.code, errinfo.msg }, errinfo.func, errinfo.file,
2✔
779
                errinfo.line, errinfo.os_code, errinfo.os_msg };
2✔
780
        }
12✔
781

782
        sirfileid add_file(const std::string& path, const sir_levels& levels,
2✔
783
            const sir_options& opts) const {
784
            const bool add = sir_addfile(path.c_str(), levels, opts);
2✔
785
            return throw_on_policy<TPolicy>(add);
2✔
786
        }
787

788
        bool rem_file(const sirfileid& id) const {
1✔
789
            const bool rem = sir_remfile(id);
1✔
790
            return throw_on_policy<TPolicy>(rem);
1✔
791
        }
792

793
        sirpluginid load_plugin(const std::string& path) const {
794
            const bool load = sir_loadplugin(path.c_str());
795
            return throw_on_policy<TPolicy>(load);
796
        }
797

798
        bool unload_plugin(const sirpluginid& id) const {
799
            const bool unload = sir_unloadplugin(id);
800
            return throw_on_policy<TPolicy>(unload);
801
        }
802

803
        bool set_file_levels(const sirfileid& id, const sir_levels& levels) const {
804
            const bool set = sir_filelevels(id, levels);
805
            return throw_on_policy<TPolicy>(set);
806
        }
807

808
        bool set_file_options(const sirfileid& id, const sir_options& opts) const {
809
            const bool set = sir_fileopts(id, opts);
810
            return throw_on_policy<TPolicy>(set);
811
        }
812

813
        bool set_text_style(const sir_level& level, const sir_textattr& attr,
814
            const sir_textcolor& fg, const sir_textcolor& bg) const {
815
            const bool set = sir_settextstyle(level, attr, fg, bg);
816
            return throw_on_policy<TPolicy>(set);
817
        }
818

819
        bool reset_text_styles() const {
820
            const bool reset = sir_resettextstyles();
821
            return throw_on_policy<TPolicy>(reset);
822
        }
823

824
        sir_textcolor make_rgb(const sir_textcolor& r, const sir_textcolor& g,
825
            const sir_textcolor& b) const {
826
            const bool make = sir_makergb(r, g, b);
827
            return throw_on_policy<TPolicy>(make);
828
        }
829

830
        bool set_color_mode(const sir_colormode& mode) const {
831
            const bool set = sir_setcolormode(mode);
832
            return throw_on_policy<TPolicy>(set);
833
        }
834

835
        bool set_stdout_levels(const sir_levels& levels) const {
836
            const bool set = sir_stdoutlevels(levels);
837
            return throw_on_policy<TPolicy>(set);
838
        }
839

840
        bool set_stdout_options(const sir_options& opts) const {
841
            const bool set = sir_stdoutopts(opts);
842
            return throw_on_policy<TPolicy>(set);
843
        }
844

845
        bool set_stderr_levels(const sir_levels& levels) const {
846
            const bool set = sir_stderrlevels(levels);
847
            return throw_on_policy<TPolicy>(set);
848
        }
849

850
        bool set_stderr_options(const sir_options& opts) const {
851
            const bool set = sir_stderropts(opts);
852
            return throw_on_policy<TPolicy>(set);
853
        }
854

855
        bool set_syslog_levels(const sir_levels& levels) const {
856
            const bool set = sir_sysloglevels(levels);
857
            return throw_on_policy<TPolicy>(set);
858
        }
859

860
        bool set_syslog_options(const sir_options& opts) const {
861
            const bool set = sir_syslogopts(opts);
862
            return throw_on_policy<TPolicy>(set);
863
        }
864

865
        bool set_syslog_id(const std::string& id) const {
866
            const bool set = sir_syslogid(id.c_str());
867
            return throw_on_policy<TPolicy>(set);
868
        }
869

870
        bool set_syslog_category(const std::string& category) const {
871
            const bool set = sir_syslogcat(category.c_str());
872
            return throw_on_policy<TPolicy>(set);
873
        }
874

875
        std::string get_version_string() const {
876
            return sir_getversionstring();
877
        }
878

879
        uint32_t get_version_hex() const noexcept {
880
            return sir_getversionhex();
881
        }
882

883
        bool is_prerelease() const noexcept {
884
            return sir_isprerelease();
885
        }
886

887
    protected:
888
        bool _init() const noexcept {
20✔
889
            TPolicy policy {};
20✔
890
            sirinit si {};
20✔
891
            if (const bool init = policy.get_init_data(si) && sir_init(&si) &&
38✔
892
                policy.on_init_complete(); !init) {
38✔
893
                return false;
2✔
894
            }
895
            return true;
18✔
896
        }
20✔
897
    };
898

899
    /**
900
     * @typedef default_logger
901
     * @brief A logger that implements the default adapter.
902
     */
903
    using default_logger = logger<true, default_policy, default_adapter>;
904

905
# if defined(__SIR_HAVE_STD_FORMAT__)
906
    using std_format_logger = logger
907
    <
908
        true,
909
        default_policy,
910
        default_adapter,
911
        std_format_adapter
912
    >;
913
# endif
914

915
# if defined(__SIR_HAVE_BOOST_FORMAT__)
916
    using boost_logger = logger
917
    <
918
        true,
919
        default_policy,
920
        default_adapter,
921
        boost_format_adapter
922
    >;
923
# endif
924

925
# if defined(__SIR_HAVE_FMT_FORMAT__)
926
    using fmt_logger = logger
927
    <
928
        true,
929
        default_policy,
930
        default_adapter,
931
        fmt_format_adapter
932
    >;
933
# endif
934

935
# if !defined(SIR_NO_STD_IOSTREAM)
936
    using iostream_logger = logger
937
    <
938
        true,
939
        default_policy,
940
        default_adapter,
941
        std_iostream_adapter
942
    >;
943
# endif
944

945
} // ! namespace sir
946

947
/**
948
 * @}
949
 * @}
950
 */
951

952
#endif // !_SIR_HH_INCLUDED
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc