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

aremmell / libsir / 834

22 Sep 2023 05:53PM UTC coverage: 94.27% (-0.3%) from 94.537%
834

Pull #292

gitlab-ci

johnsonjh
Update since gcc version 12.3.0 (OpenIndiana 12.3.0-oi-1) on OpenIndiana works

Signed-off-by: Jeffrey H. Johnson <trnsz@pobox.com>
Pull Request #292: Python/Ch bindings (& general support for new bindings), WIP C++ header-only wrapper implementation

877 of 877 new or added lines in 9 files covered. (100.0%)

3570 of 3787 relevant lines covered (94.27%)

530685.13 hits per line

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

97.97
/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 = 0;   /**< The error code associated with the message. */
79
        std::string message; /**< Description of the error that occurred. */
80
    };
81

82
    /**
83
     * @class exception
84
     * @brief Derives from std::runtime_error in order to be caught easily by
85
     * standard try/catch blocks, as well as to provide useful error messages
86
     * by way of the what() method.
87
     */
88
    class exception : public std::runtime_error {
89
    public:
90
        using std::runtime_error::runtime_error;
91

92
        exception() = delete;
93
        exception(const error& err) : exception(err.message) { }
3✔
94
        ~exception() override = default;
8✔
95

96
        static exception from_libsir_error() {
2✔
97
            std::array<char, SIR_MAXERROR> msg {};
2✔
98
            const auto code = sir_geterror(msg.data());
2✔
99
            return exception({ code, msg.data() });
4✔
100
        }
6✔
101
    };
102

103
    /**
104
     * @class policy
105
     * @brief Defines the partially abstract interface for a policy which
106
     * controls the behavior of logger at runtime.
107
     */
108
    class policy {
109
    public:
110
        policy() = default;
111
        virtual ~policy() = default;
20✔
112

113
        /**
114
         * Called by logger before initializing libsir. Provides the policy with
115
         * an opportunity to customize the initial libsir configuration.
116
         *
117
         * @param data initial libisr configuration data.
118
         *
119
         * @see ::sirinit
120
         * @see ::sir_makeinit
121
         *
122
         * @note Most, if not all of the options chosen during initialization
123
         * can be modified at runtime using functions provided by libsir.
124
         *
125
         * @note For the default configuration, pass the address of the ::sirinit
126
         * structure to ::sir_makeinit.
127
         *
128
         * @returns true if data was successfully configured, or false if an
129
         * error occurred. Returning false will cause logger to abort the
130
         * initialization process–either by throwing an exception, or by entering
131
         * an 'invalid' state (determined by policy::throw_on_error).
132
         */
133
        virtual bool get_init_data(sirinit& data) const noexcept = 0;
134

135
        /**
136
         * Called by logger immediately after libsir has been initialized. This
137
         * is the moment in time which should be utilized for further configuration
138
         * than is possible via ::sirinit alone.
139
         *
140
         * Some items include:
141
         * - Setting the ::sir_colormode for stdio (::sir_setcolormode)
142
         * - Setting ::sir_textstyle on a per-level basis for stdio (::sir_settextstyle)
143
         * - Adding log files (::sir_addfile)
144
         * - Loading plugins (::sir_loadplugin)
145
         *
146
         * @returns true if configuration was completed successfully, or false if
147
         * an error occurred. Returning false will cause logger to abort the
148
         * initialization process–either by throwing an exception, or by entering
149
         * an 'invalid' state (determined by policy::throw_on_error).
150
         */
151
        virtual bool on_init_complete() const noexcept = 0;
152

153
        /**
154
         * Determines whether or not exceptions are thrown in the event that a
155
         * libsir or OS-level error is encountered by logger or its associated
156
         * adapter(s).
157
         *
158
         * @note If exceptions are not enabled, the caller of a method that could
159
         * potentially fail is responsible for obtaining error information by
160
         * calling logger::get_error and reacting accordingly.
161
         *
162
         * @returns true if logger should throw exceptions (derived from std::
163
         * exception) when errors are encountered, or false if errors should
164
         * simply be communicated through a pass/fail Boolean return value.
165
         */
166
        static constexpr bool throw_on_error() noexcept {
167
            return false;
168
        }
169

170
        /**
171
         * Useful when complications arise; the question "which policy caused that
172
         * to happen?!" is then easy to answer.
173
         *
174
         * @returns A unique name for the policy.
175
         */
176
        virtual constexpr const char* get_name() const = 0;
177
    };
178

179
    /** Ensures that T derives from policy. */
180
    template<typename T>
181
    concept DerivedFromPolicy = std::is_base_of_v<policy, T>;
182

183
    /**
184
     * @class default_policy
185
     * @brief In the event that no custom configuration or behavior is desired,
186
     * provides defaults for everything.
187
     *
188
     * - Uses all default values for sir_init
189
     * - Performs no post-initialization configuration
190
     * - Exceptions are thrown when errors are encountered
191
     */
192
    class default_policy : public policy {
193
    public:
194
        default_policy() = default;
195
        ~default_policy() override = default;
20✔
196

197
        bool get_init_data(sirinit& data) const noexcept final {
10✔
198
            return sir_makeinit(&data);
10✔
199
        }
200

201
        constexpr bool on_init_complete() const noexcept final {
9✔
202
            return true;
9✔
203
        }
204

205
        static constexpr bool throw_on_error() noexcept {
206
            return true;
207
        }
208

209
        constexpr const char* get_name() const final {
×
210
            return "Default";
×
211
        }
212
    };
213

214
    /**
215
     * @class adapter
216
     * @brief Defines the abstract interface for an adapter, which ultimately
217
     * becomes a public base class of logger (there can be more than one).
218
     *
219
     * adapter is designed to provide flexibility and extensibility in relation
220
     * to the public interface that is implemented by a logger.
221
     *
222
     * @note One must take care to ensure that the methods implemented by an
223
     * adapter can coexist with any other adapters that are applied to the logger
224
     * template.
225
     *
226
     * @see ::std_format_adapter
227
     * @see ::boost_format_adapter
228
     * @see ::fmt_format_adapter
229
     * @see ::std_iostream_adapter
230
     */
231
    class adapter {
232
    protected:
233
        adapter() = default;
14✔
234
        virtual ~adapter() = default;
28✔
235
    };
236

237
    /**
238
     * @brief Handles a potential error; if an error is present and the policy
239
     * in place requires throwing an exception, retrieves the associated error
240
     * code and message from libsir, then throws.
241
     *
242
     * @param TPolicy policy A derived class of policy which determines whether
243
     * or not to throw exceptions upon encountering an error.
244
     *
245
     * @param expr bool An expression that is evaluated against false. If false,
246
     *  an error is determined to have occurred and an exception will be thrown
247
     * if the policy requires it.
248
     *
249
     * @returns bool If no exception was thrown, the return value is expr (true
250
     * if no error occurred, false otherwise).
251
     */
252
    template<DerivedFromPolicy TPolicy>
253
    static inline bool throw_on_policy(bool expr) noexcept(false) {
70✔
254
        if (!expr) {
70✔
255
            SIR_ASSERT(expr);
2✔
256
            if constexpr(TPolicy::throw_on_error()) {
257
                throw exception::from_libsir_error();
2✔
258
            }
259
        }
260
        return expr;
68✔
261
    }
262

263
    /**
264
     * @class default_adapter
265
     * @brief The default adapter implementation.
266
     *
267
     * Utilizes the same code path that the C interface itself does, in order to
268
     * achieve maximum performance (i.e., no unnecessary bloat is added).
269
     *
270
     * @param TPolicy policy A derived class of policy which controls the behavior
271
     * of logger and by association, its adapters.
272
     */
273
    template<DerivedFromPolicy TPolicy>
274
    class default_adapter : public adapter {
275
    public:
276
        default_adapter() = default;
10✔
277
        ~default_adapter() override = default;
20✔
278

279
        /** Logs a debug message (see ::sir_debug). */
280
        PRINTF_FORMAT_ATTR(2, 3)
281
        bool debug(PRINTF_FORMAT const char* format, ...) const {
7✔
282
            _SIR_L_START(format);
7✔
283
            ret = _sir_logv(SIRL_DEBUG, format, args);
6✔
284
            _SIR_L_END();
6✔
285
            return throw_on_policy<TPolicy>(ret);
6✔
286
        }
287

288
        /** Logs an informational message (see ::sir_info). */
289
        PRINTF_FORMAT_ATTR(2, 3)
290
        bool info(PRINTF_FORMAT const char* format, ...) const {
1✔
291
            _SIR_L_START(format);
1✔
292
            ret = _sir_logv(SIRL_INFO, format, args);
1✔
293
            _SIR_L_END();
1✔
294
            return throw_on_policy<TPolicy>(ret);
1✔
295
        }
296

297
        /** Logs a notice message (see ::sir_notice). */
298
        PRINTF_FORMAT_ATTR(2, 3)
299
        bool notice(PRINTF_FORMAT const char* format, ...) const {
1✔
300
            _SIR_L_START(format);
1✔
301
            ret = _sir_logv(SIRL_NOTICE, format, args);
1✔
302
            _SIR_L_END();
1✔
303
            return throw_on_policy<TPolicy>(ret);
1✔
304
        }
305

306
        /** Logs a warning message (see ::sir_warn). */
307
        PRINTF_FORMAT_ATTR(2, 3)
308
        bool warn(PRINTF_FORMAT const char* format, ...) const {
1✔
309
            _SIR_L_START(format);
1✔
310
            ret = _sir_logv(SIRL_WARN, format, args);
1✔
311
            _SIR_L_END();
1✔
312
            return throw_on_policy<TPolicy>(ret);
1✔
313
        }
314

315
        /** Logs an error message (see ::sir_error). */
316
        PRINTF_FORMAT_ATTR(2, 3)
317
        bool error(PRINTF_FORMAT const char* format, ...) const {
1✔
318
            _SIR_L_START(format);
1✔
319
            ret = _sir_logv(SIRL_ERROR, format, args);
1✔
320
            _SIR_L_END();
1✔
321
            return throw_on_policy<TPolicy>(ret);
1✔
322
        }
323

324
        /** Logs a critical message (see ::sir_crit). */
325
        PRINTF_FORMAT_ATTR(2, 3)
326
        bool crit(PRINTF_FORMAT const char* format, ...) const {
1✔
327
            _SIR_L_START(format);
1✔
328
            ret = _sir_logv(SIRL_CRIT, format, args);
1✔
329
            _SIR_L_END();
1✔
330
            return throw_on_policy<TPolicy>(ret);
1✔
331
        }
332

333
        /** Logs an alert message (see ::sir_alert). */
334
        PRINTF_FORMAT_ATTR(2, 3)
335
        bool alert(PRINTF_FORMAT const char* format, ...) const {
1✔
336
            _SIR_L_START(format);
1✔
337
            ret = _sir_logv(SIRL_ALERT, format, args);
1✔
338
            _SIR_L_END();
1✔
339
            return throw_on_policy<TPolicy>(ret);
1✔
340
        }
341

342
        /** Logs an emergency message (see ::sir_emerg). */
343
        PRINTF_FORMAT_ATTR(2, 3)
344
        bool emerg(PRINTF_FORMAT const char* format, ...) const {
1✔
345
            _SIR_L_START(format);
1✔
346
            ret = _sir_logv(SIRL_EMERG, format, args);
1✔
347
            _SIR_L_END();
1✔
348
            return throw_on_policy<TPolicy>(ret);
1✔
349
        }
350
    };
351

352
# if defined(__SIR_HAVE_STD_FORMAT__)
353
    /**
354
     * @class std_format_adapter
355
     * @brief Adapter for std::format (when available).
356
     *
357
     * TODO: update description
358
     *
359
     * @param TPolicy policy A derived class of policy which controls the behavior
360
     * of logger and by association, its adapters.
361
     */
362
    template<DerivedFromPolicy TPolicy>
363
    class std_format_adapter : public adapter {
364
    public:
365
        std_format_adapter() = default;
1✔
366
        ~std_format_adapter() override = default;
2✔
367

368
        template<typename... Args>
369
        inline bool debug_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
370
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
371
            const bool ret = sir_debug("%s", str.c_str());
1✔
372
            return throw_on_policy<TPolicy>(ret);
2✔
373
        }
1✔
374

375
        template<typename... Args>
376
        inline bool info_std(std::format_string<Args...> fmt, Args&&... args) const {
1✔
377
            const auto str = std::vformat(fmt.get(), std::make_format_args(args...));
2✔
378
            const bool ret = sir_info("%s", str.c_str());
1✔
379
            return throw_on_policy<TPolicy>(ret);
2✔
380
        }
1✔
381

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

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

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

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

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

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

426
# if defined(__SIR_HAVE_BOOST_FORMAT__)
427
    /**
428
     * @class boost_format_adapter
429
     * @brief Adapter for boost::format (when available).
430
     *
431
     * TODO: update description
432
     *
433
     * @param TPolicy policy A derived class of policy which controls the behavior
434
     * of logger and by association, its adapters.
435
     */
436
    template<DerivedFromPolicy TPolicy>
437
    class boost_format_adapter : public adapter {
438
    public:
439
        boost_format_adapter() = default;
1✔
440
        ~boost_format_adapter() override = default;
2✔
441

442
        bool debug_boost(const boost::format& fmt) const {
1✔
443
            const bool ret = sir_debug("%s", fmt.str().c_str());
1✔
444
            return throw_on_policy<TPolicy>(ret);
1✔
445
        }
446

447
        bool info_boost(const boost::format& fmt) const {
1✔
448
            const bool ret = sir_info("%s", fmt.str().c_str());
1✔
449
            return throw_on_policy<TPolicy>(ret);
1✔
450
        }
451

452
        bool notice_boost(const boost::format& fmt) const {
1✔
453
            const bool ret = sir_notice("%s", fmt.str().c_str());
1✔
454
            return throw_on_policy<TPolicy>(ret);
1✔
455
        }
456

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

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

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

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

477
        bool emerg_boost(const boost::format& fmt) const {
1✔
478
            const bool ret = sir_emerg("%s", fmt.str().c_str());
1✔
479
            return throw_on_policy<TPolicy>(ret);
1✔
480
        }
481
    };
482
# endif // !__SIR_HAVE_BOOST_FORMAT__
483

484
# if defined(__SIR_HAVE_FMT_FORMAT__)
485
    /**
486
     * @class fmt_format_adapter
487
     * @brief Adapter for fmt (when available).
488
     *
489
     * TODO: update description
490
     *
491
     * @param TPolicy policy A derived class of policy which controls the behavior
492
     * of logger and by association, its adapters.
493
     */
494
    template<DerivedFromPolicy TPolicy>
495
    class fmt_format_adapter : public adapter {
496
    public:
497
        fmt_format_adapter() = default;
1✔
498
        ~fmt_format_adapter() override = default;
2✔
499

500
        template<typename... Args>
501
        inline bool debug_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
502
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
503
            const auto ret = sir_debug("%s", str.c_str());
1✔
504
            return throw_on_policy<TPolicy>(ret);
2✔
505
        }
1✔
506

507
        template<typename... Args>
508
        inline bool info_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
509
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
510
            const auto ret = sir_info("%s", str.c_str());
1✔
511
            return throw_on_policy<TPolicy>(ret);
2✔
512
        }
1✔
513

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

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

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

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

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

549
        template<typename... Args>
550
        inline bool emerg_fmt(fmt::format_string<Args...> fmt, Args&&... args) const {
1✔
551
            const auto str = fmt::vformat(fmt, fmt::make_format_args(args...));
2✔
552
            const auto ret = sir_emerg("%s", str.c_str());
1✔
553
            return throw_on_policy<TPolicy>(ret);
2✔
554
        }
1✔
555
    };
556
# endif // !__SIR_HAVE_FMT_FORMAT__
557

558
# if !defined(SIR_NO_STD_IOSTREAM)
559
    /** Ensures that T derives from std::streambuf. */
560
    template<typename T>
561
    concept DerivedFromStreamBuf = std::is_base_of_v<std::streambuf, T>;
562

563
    /**
564
     * @class std_iostream_adapter
565
     * @brief Provides a std::iostream interface to libsir's logging functions.
566
     *
567
     * Implements a public std::ostream member for each available logging level
568
     * (e.g. debug_stream, info_stream, ..., emerg_stream).
569
     *
570
     * @note Use std::endl or std::flush to indicate the end of a log message if
571
     * using the iostream << operator.
572
     *
573
     * @param TPolicy policy A derived class of policy which controls the behavior
574
     * of logger and by association, its adapters.
575
     */
576
    template<DerivedFromPolicy TPolicy>
577
    class std_iostream_adapter : public adapter {
578
    public:
579
        std_iostream_adapter() = default;
1✔
580
        ~std_iostream_adapter() override = default;
2✔
581

582
        template<typename TFunc>
583
        class buffer : public std::streambuf {
584
        public:
585
            using array_type = std::array<char_type, SIR_MAXMESSAGE>;
586

587
            buffer() = delete;
588

589
            explicit buffer(TFunc pfn) : _pfn(pfn) {
8✔
590
                [[maybe_unused]]
591
                bool ok = throw_on_policy<TPolicy>(pfn != nullptr && _arr != nullptr);
8✔
592
                reset();
8✔
593
            }
8✔
594

595
            ~buffer() override = default;
32✔
596

597
        protected:
598
            void reset() {
23✔
599
                if (_arr) {
23✔
600
                    _arr->fill('\0');
23✔
601
                    setp(_arr->data(), _arr->data() + _arr->size() - 1);
92✔
602
                }
603
            }
23✔
604

605
            bool write_out() {
15✔
606
                for (auto it = _arr->rbegin(); it != _arr->rend(); it++) {
61,076✔
607
                    if ((*it != '\0') && (*it == '\n')) {
61,072✔
608
                        *it = '\0';
11✔
609
                        break;
11✔
610
                    }
611
                }
612

613
                const bool write = _pfn ? _pfn(_arr->data()) : false;
30✔
614
                reset();
15✔
615
                return throw_on_policy<TPolicy>(write);
15✔
616
            }
617

618
            int_type overflow(int_type ch) override {
1✔
619
                if (ch != traits_type::eof() && write_out()) {
1✔
620
                    return ch;
1✔
621
                }
622
                return traits_type::eof();
×
623
            }
624

625
            int sync() override {
14✔
626
                return write_out() ? 0 : -1;
14✔
627
            }
628

629
            std::streamsize xsputn(const char_type* s, std::streamsize count) override {
21✔
630
                std::streamsize written = 0;
21✔
631
                do {
632
                    ptrdiff_t left = epptr() - pptr();
23✔
633
                    if (!left) {
23✔
634
                        int_type ch = overflow(traits_type::to_int_type(*(s + written)));
1✔
635
                        if (ch != traits_type::eof()) {
1✔
636
                            continue;
1✔
637
                        } else {
638
                            break;
×
639
                        }
640
                    }
641

642
                    auto this_write = std::min(static_cast<std::streamsize>(left),
44✔
643
                        count - written);
22✔
644
                    memcpy(pptr(), s + written, static_cast<size_t>(this_write));
22✔
645
                    pbump(static_cast<int>(this_write));
22✔
646
                    written += this_write;
22✔
647
                } while (written < count);
23✔
648

649
                return written;
21✔
650
            }
651

652
        private:
653
            std::shared_ptr<array_type> _arr = std::make_shared<array_type>();
654
            TFunc _pfn = nullptr;
655
        };
656

657
        template<DerivedFromStreamBuf TBuffer>
658
        class stream : public std::ostream {
659
        public:
660
            explicit stream(const TBuffer& buf) : std::ostream(&_buf), _buf(buf) {}
8✔
661
            ~stream() override = default;
16✔
662

663
        private:
664
            TBuffer _buf;
665
        };
666

667
        using log_func = bool(*)(const char*, ...);
668
        using buffer_type = buffer<log_func>;
669
        using stream_type = stream<buffer_type>;
670

671
        stream_type debug_stream  {buffer_type {&sir_debug}};
672
        stream_type info_stream   {buffer_type {&sir_info}};
673
        stream_type notice_stream {buffer_type {&sir_notice}};
674
        stream_type warn_stream   {buffer_type {&sir_warn}};
675
        stream_type error_stream  {buffer_type {&sir_error}};
676
        stream_type crit_stream   {buffer_type {&sir_crit}};
677
        stream_type alert_stream  {buffer_type {&sir_alert}};
678
        stream_type emerg_stream  {buffer_type {&sir_emerg}};
679
    };
680
# endif // SIR_NO_STD_IOSTREAM
681

682
    /** Utility template for obtaining the type of Nth item in a parameter pack. */
683
    template<size_t N, typename ...Ts>
684
    using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type;
685

686
    /** Ensures (at compile time) that all Ts are derived classes of TBase. */
687
    template<size_t N, typename TBase, typename ...Ts>
688
    consteval auto all_derived_from_t() {
689
        if constexpr(!std::is_base_of_v<TBase, NthTypeOf<N, Ts...>>)
690
            return std::false_type::value;
691
        if constexpr(N == 0)
692
            return std::true_type::value;
693
        else
694
            return all_derived_from_t<N - 1, TBase, Ts...>();
695
    }
696

697
    /** True if all Ts are derived classes of TBase, otherwise false. */
698
    template<class TBase, class ...Ts>
699
    concept DerivedFromT = all_derived_from_t<sizeof...(Ts) - 1, TBase, Ts...>();
700

701
    /**
702
     * @class logger
703
     * @brief The primary C++ interface to libsir.
704
     *
705
     * Instantiate this class in order to access libsir with all the benefits
706
     * of C++, including RAII initialization/cleanup, custom adapters, and more.
707
     *
708
     * @param RAII      Set to `true` to enable 'resource acquisition is
709
     *                  initialization' behavior (i.e., libsir is initialized by
710
     *                  the ctor, and cleaned up by the dtor). Set to `false` for
711
     *                  manual management of initialization/cleanup.
712
     * @param TPolicy   A policy class which will be responsible for certain
713
     *                  aspects of logger's behavior.
714
     * @param TAdapters One or more adapter classes whose public methods will be
715
     *                  exposed by this class.
716
     */
717
    template<bool RAII, DerivedFromPolicy TPolicy, template<class> class ...TAdapters>
718
    requires DerivedFromT<adapter, TAdapters<TPolicy>...>
719
    class logger : public TAdapters<TPolicy>... {
720
    public:
721
        logger() : TAdapters<TPolicy>()... {
20✔
722
            if constexpr(RAII) {
723
                [[maybe_unused]]
724
                bool ok = throw_on_policy<TPolicy>(_init());
18✔
725
            }
726
        }
20✔
727

728
        logger(const logger&) = delete;
729
        logger(const logger&&) = delete;
730

731
        ~logger() override {
18✔
732
            if constexpr(RAII) {
733
                if (!cleanup()) {
16✔
734
                    SIR_ASSERT(false);
×
735
                }
736
            }
737
        }
34✔
738

739
        logger& operator=(const logger&) = delete;
740
        logger& operator=(const logger&&) = delete;
741

742
        bool init() const noexcept {
1✔
743
            return is_initialized() ? false: _init();
1✔
744
        }
745

746
        bool cleanup() const noexcept {
18✔
747
            return is_initialized() ? sir_cleanup() : false;
18✔
748
        }
749

750
        bool is_initialized() const noexcept {
20✔
751
            return sir_isinitialized();
20✔
752
        }
753

754
        error get_error() const {
1✔
755
            std::array<char, SIR_MAXERROR> message {};
1✔
756
            const auto code = sir_geterror(message.data());
1✔
757
            return { code, message.data() };
2✔
758
        }
3✔
759

760
        sirfileid add_file(const std::string& path, const sir_levels& levels,
1✔
761
            const sir_options& opts) const {
762
            const bool add = sir_addfile(path.c_str(), levels, opts);
1✔
763
            return throw_on_policy<TPolicy>(add);
1✔
764
        }
765

766
        bool rem_file(const sirfileid& id) const {
767
            const bool rem = sir_remfile(id);
768
            return throw_on_policy<TPolicy>(rem);
769
        }
770

771
        sirpluginid load_plugin(const std::string& path) const {
772
            const bool load = sir_loadplugin(path.c_str());
773
            return throw_on_policy<TPolicy>(load);
774
        }
775

776
        bool unload_plugin(const sirpluginid& id) const {
777
            const bool unload = sir_unloadplugin(id);
778
            return throw_on_policy<TPolicy>(unload);
779
        }
780

781
        bool set_file_levels(const sirfileid& id, const sir_levels& levels) const {
782
            const bool set = sir_filelevels(id, levels);
783
            return throw_on_policy<TPolicy>(set);
784
        }
785

786
        bool set_file_options(const sirfileid& id, const sir_options& opts) const {
787
            const bool set = sir_fileopts(id, opts);
788
            return throw_on_policy<TPolicy>(set);
789
        }
790

791
        bool set_text_style(const sir_level& level, const sir_textattr& attr,
792
            const sir_textcolor& fg, const sir_textcolor& bg) const {
793
            const bool set = sir_settextstyle(level, attr, fg, bg);
794
            return throw_on_policy<TPolicy>(set);
795
        }
796

797
        bool reset_text_styles() const {
798
            const bool reset = sir_resettextstyles();
799
            return throw_on_policy<TPolicy>(reset);
800
        }
801

802
        sir_textcolor make_rgb(const sir_textcolor& r, const sir_textcolor& g,
803
            const sir_textcolor& b) const {
804
            const bool make = sir_makergb(r, g, b);
805
            return throw_on_policy<TPolicy>(make);
806
        }
807

808
        bool set_color_mode(const sir_colormode& mode) const {
809
            const bool set = sir_setcolormode(mode);
810
            return throw_on_policy<TPolicy>(set);
811
        }
812

813
        bool set_stdout_levels(const sir_levels& levels) const {
814
            const bool set = sir_stdoutlevels(levels);
815
            return throw_on_policy<TPolicy>(set);
816
        }
817

818
        bool set_stdout_options(const sir_options& opts) const {
819
            const bool set = sir_stdoutopts(opts);
820
            return throw_on_policy<TPolicy>(set);
821
        }
822

823
        bool set_stderr_levels(const sir_levels& levels) const {
824
            const bool set = sir_stderrlevels(levels);
825
            return throw_on_policy<TPolicy>(set);
826
        }
827

828
        bool set_stderr_options(const sir_options& opts) const {
829
            const bool set = sir_stderropts(opts);
830
            return throw_on_policy<TPolicy>(set);
831
        }
832

833
        bool set_syslog_levels(const sir_levels& levels) const {
834
            const bool set = sir_sysloglevels(levels);
835
            return throw_on_policy<TPolicy>(set);
836
        }
837

838
        bool set_syslog_options(const sir_options& opts) const {
839
            const bool set = sir_syslogopts(opts);
840
            return throw_on_policy<TPolicy>(set);
841
        }
842

843
        bool set_syslog_id(const std::string& id) const {
844
            const bool set = sir_syslogid(id.c_str());
845
            return throw_on_policy<TPolicy>(set);
846
        }
847

848
        bool set_syslog_category(const std::string& category) const {
849
            const bool set = sir_syslogcat(category.c_str());
850
            return throw_on_policy<TPolicy>(set);
851
        }
852

853
        std::string get_version_string() const {
854
            return sir_getversionstring();
855
        }
856

857
        uint32_t get_version_hex() const noexcept {
858
            return sir_getversionhex();
859
        }
860

861
        bool is_prerelease() const noexcept {
862
            return sir_isprerelease();
863
        }
864

865
    protected:
866
        bool _init() const noexcept {
20✔
867
            TPolicy policy {};
20✔
868
            sirinit si {};
20✔
869
            if (const bool init = policy.get_init_data(si) && sir_init(&si) &&
38✔
870
                policy.on_init_complete(); !init) {
38✔
871
                return false;
2✔
872
            }
873
            return true;
18✔
874
        }
20✔
875
    };
876

877
    /**
878
     * @typedef default_logger
879
     * @brief A logger that implements the default adapter.
880
     */
881
    using default_logger = logger<true, default_policy, default_adapter>;
882

883
# if defined(__SIR_HAVE_STD_FORMAT__)
884
    using std_format_logger = logger
885
    <
886
        true,
887
        default_policy,
888
        default_adapter,
889
        std_format_adapter
890
    >;
891
# endif
892

893
# if defined(__SIR_HAVE_BOOST_FORMAT__)
894
    using boost_logger = logger
895
    <
896
        true,
897
        default_policy,
898
        default_adapter,
899
        boost_format_adapter
900
    >;
901
# endif
902

903
# if defined(__SIR_HAVE_FMT_FORMAT__)
904
    using fmt_logger = logger
905
    <
906
        true,
907
        default_policy,
908
        default_adapter,
909
        fmt_format_adapter
910
    >;
911
# endif
912

913
# if !defined(SIR_NO_STD_IOSTREAM)
914
    using iostream_logger = logger
915
    <
916
        true,
917
        default_policy,
918
        default_adapter,
919
        std_iostream_adapter
920
    >;
921
# endif
922

923
} // ! namespace sir
924

925
/**
926
 * @}
927
 * @}
928
 */
929

930
#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