GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/logger.h
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 14 16 87.5%
Functions: 5 6 83.3%
Branches: 1 6 16.7%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_LOGGER
41 #define GEOGRAM_BASIC_LOGGER
42
43 #ifdef __cplusplus
44
45 #include <geogram/basic/common.h>
46 #include <geogram/basic/environment.h>
47 #include <geogram/basic/process.h>
48 #include <iostream>
49 #include <fstream>
50 #include <sstream>
51 #include <string>
52 #include <set>
53 #include <stdlib.h>
54
55 /**
56 * \file geogram/basic/logger.h
57 * \brief Generic logging mechanism
58 */
59
60 namespace GEO {
61
62 class Logger;
63 class LoggerStream;
64
65 /**
66 * \brief Stream buffer used by the LoggerStream%s
67 * \details This class is used internally to implement the logger
68 * mechanism. Since it inherits a STL class, it is declared as
69 * NO_GEOGRAM_API so that it is not exported when Windows DLLs
70 * are generated (doing otherwise would generate multiply defined
71 * symbols).
72 */
73 class NO_GEOGRAM_API LoggerStreamBuf : public std::stringbuf {
74 public:
75 /**
76 * \brief Creates a Logger stream buffer
77 * \details Creates a LoggerStreamBuf associated to the LoggerStream
78 * \p loggerStream
79 * \param[in] loggerStream the LoggerStream that owns this buffer
80 */
81 996 LoggerStreamBuf(LoggerStream* loggerStream) :
82 996 loggerStream_(loggerStream) {
83 996 }
84
85 private:
86 /**
87 * \brief Synchronizes stream buffer
88 * \details Reimplementation of function std::stringbuf::sync() that
89 * sends the character sequence to the LoggerStream
90 * \retval zero, on success.
91 * \retval -1 on failure.
92 * \see LoggerStream::notify()
93 */
94 int sync() override;
95
96 private:
97 LoggerStream* loggerStream_;
98 };
99
100 /************************************************************************/
101
102 /**
103 * \brief Stream used by the Logger
104 * \details This class is used used internally to implement logger
105 * mechanism. Since it inherits a STL class, it is declared as
106 * NO_GEOGRAM_API so that it is not exported when Windows DLLs
107 * are generated (doing otherwise would generate multiply defined
108 * symbols).
109 */
110 class NO_GEOGRAM_API LoggerStream : public std::ostream {
111 public:
112 /**
113 * \brief Creates a Logger stream
114 * \details Creates a LoggerStream associated to the Logger \p logger
115 * \param[in] logger the Logger that owns this stream
116 */
117 LoggerStream(Logger* logger);
118
119 /**
120 * \brief Logger stream destructor
121 */
122 ~LoggerStream() override;
123
124 protected:
125 /**
126 * \brief Sends a string to the Logger
127 * \details This function is called by LoggerStreamBuf::sync() when a
128 * sequence of characters \p str is available in the stream. This
129 * sequence is sent back to the logger to deliver to the
130 * LoggerClient%s
131 * \param[in] str the sequence of characters to send
132 * \see LoggerStreamBuf::sync()
133 */
134 void notify(const std::string& str);
135
136 private:
137 Logger* logger_;
138 friend class LoggerStreamBuf;
139 };
140
141 /************************************************************************/
142
143 /**
144 * \brief Logger client base class
145 * \details Messages sent to the Logger are sent back to registered
146 * LoggerClient%s. Logger clients must implement the following functions
147 * to handle the messages:
148 * - div() - to create a new division
149 * - out() - to handle information messages
150 * - warn() - to handle warning messages
151 * - err() - to handle error messages
152 * - status() - to handle status messages
153 * It is the responsibility of the derived LoggerClient%s to handle the
154 * various kind of messages sent by the Logger appropriately.
155 */
156 class GEOGRAM_API LoggerClient : public Counted {
157 public:
158 /**
159 * \brief Creates a new division
160 * \details This creates a new division entitled with \p title
161 * \param[in] title the text of the title
162 */
163 virtual void div(const std::string& title) = 0;
164
165 /**
166 * \brief Handles an information message
167 * \param[in] str the text of the message
168 */
169 virtual void out(const std::string& str) = 0;
170
171 /**
172 * \brief Handles a warning message
173 * \param[in] str the text of the message
174 */
175 virtual void warn(const std::string& str) = 0;
176
177 /**
178 * \brief Handles an error message
179 * \param[in] str the text of the message
180 */
181 virtual void err(const std::string& str) = 0;
182
183 /**
184 * \brief Handles a status message
185 * \param[in] str the text of the message
186 */
187 virtual void status(const std::string& str) = 0;
188
189 /**
190 * \brief LoggerClient destructor
191 */
192 ~LoggerClient() override;
193 };
194
195 /** Smart pointer that contains a LoggerClient object */
196 typedef SmartPointer<LoggerClient> LoggerClient_var;
197
198 /************************************************************************/
199
200 /**
201 * \brief Logger client that redirects messages to standard output.
202 */
203 class GEOGRAM_API ConsoleLogger : public LoggerClient {
204 public:
205 /**
206 * \brief Creates a ConsoleLogger
207 */
208 ConsoleLogger();
209
210 /**
211 * \copydoc LoggerClient::div()
212 */
213 void div(const std::string& title) override;
214
215 /**
216 * \copydoc LoggerClient::out()
217 */
218 void out(const std::string& str) override;
219
220 /**
221 * \copydoc LoggerClient::warn()
222 */
223 void warn(const std::string& str) override;
224
225 /**
226 * \copydoc LoggerClient::err()
227 */
228 void err(const std::string& str) override;
229
230 /**
231 * \copydoc LoggerClient::status()
232 * This function does actually nothing
233 */
234 void status(const std::string& str) override;
235
236 protected:
237 /**
238 * \brief ConsoleLogger destructor
239 */
240 ~ConsoleLogger() override;
241 };
242
243 /************************************************************************/
244
245 /**
246 * \brief Logger client that redirects messages to a file.
247 */
248 class GEOGRAM_API FileLogger : public LoggerClient {
249 public:
250 /**
251 * \brief Creates an empty file logger
252 * \details The default constructed file logger does not handle
253 * messages until it is set a filename with set_file_name()
254 */
255 FileLogger();
256
257 /**
258 * \brief Creates logger that logs messages to a file
259 * \details All sent to the file logger are sent to the file
260 * \p file_name.
261 * \param[in] file_name name of the log file
262 */
263 FileLogger(const std::string& file_name);
264
265 /**
266 * \copydoc LoggerClient::div()
267 */
268 void div(const std::string& title) override;
269
270 /**
271 * \copydoc LoggerClient::out()
272 */
273 void out(const std::string& str) override;
274
275 /**
276 * \copydoc LoggerClient::warn()
277 */
278 void warn(const std::string& str) override;
279
280 /**
281 * \copydoc LoggerClient::err()
282 */
283 void err(const std::string& str) override;
284
285 /**
286 * \copydoc LoggerClient::status()
287 * This function does actually nothing
288 */
289 void status(const std::string& str) override;
290
291 protected:
292 /**
293 * \brief FileLogger destructor
294 */
295 ~FileLogger() override;
296
297 /**
298 * \brief Sets the log file name
299 * \details If the client already had a file name, the corresponding
300 * file stream is closed and reopened with \p file_name.
301 * \param[in] file_name the name of the log file
302 */
303 void set_file_name(const std::string& file_name);
304
305 private:
306 std::string log_file_name_;
307 std::ostream* log_file_;
308 };
309
310 /************************************************************************/
311
312 /**
313 * \brief Generic logging framework.
314 *
315 * The Logger is a framework for logging messages with different
316 * severities to various destinations.
317 *
318 * Logging destinations can be specified by registering LoggerClient%s to
319 * the Logger (see register_client()). Predefined clients exist to log
320 * messages to a file or to the console with a pretty or standard
321 * formatting (see set_pretty()). Any number of clients can be registered
322 * to the Logger.
323 *
324 * The Logger provides 4 level of severities, each of them having its own
325 * LoggerStream:
326 * - information: out()
327 * - warning: warn()
328 * - error: err()
329 * - status: status()
330 *
331 * Thus logging a message to the specific stream is equivalent of sending
332 * a message of the correspnding severity. For instance, logging a message
333 * to the warn() stream means sending a warning message to the Logger.
334 *
335 * The Logger also provides a pseudo stream div() that creates a division
336 * in the log output, that is the log can be structured in kind of
337 * chapters introduced by a heading title.
338 *
339 * Messages are associated to features. A feature can be considered as the
340 * source context (eg: messages sent to the Logger by the mesh I/O module
341 * specify feature "I/O"). The feature is specified when selecting the
342 * stream to. When a message is sent to the LoggerClient%s by the Logger,
343 * it contains information about its severity and the associated feature.
344 *
345 * Features are not only information about of the message source, they
346 * also support a message filtering mechanism. Specific features can be
347 * enabled by setting the Logger property \e log:features to a
348 * colon-separated list of enabled feature names, or disabled by setting
349 * the Logger property \e log:features_exclude to a colon-separated list
350 * of excluded feature names (see set_value()). Note that setting property
351 * \e log:features to the special value "*" globally enables all logging
352 * features (this is the default).
353 *
354 * The Logger can also be turned off temporarily by setting the quiet mode
355 * to \c true, which disables all messages, warnings and errors included
356 * (set set_quiet()).
357 */
358 class GEOGRAM_API Logger : public Environment {
359 public:
360 /**
361 * \brief Initializes the logging system
362 * \details This function must be called once at program startup to
363 * create and initialize the Logger instance. It is called by
364 * GEO::initialize().
365 * \see instance()
366 */
367 static void initialize();
368
369 /**
370 * \brief Terminates the logging system
371 * \details This function must be called once when the program ends to
372 * delete the Logger instance. It is called by GEO::terminate()
373 * \see instance()
374 */
375 static void terminate();
376
377 /**
378 * \brief Returns the Logger single instance
379 * \details This function does \b not create the Logger instance.
380 * Calling instance() before initialize() has been called returns a \c
381 * null pointer. Similarly, calling instance() after terminate()
382 * has been called returns a \c null pointer.
383 * \return A pointer to the Logger if initialized, null otherwise
384 * \see initialize()
385 * \see terminate()
386 */
387 static Logger* instance();
388
389
390 /**
391 * \brief Tests whether the Logger is initialized.
392 * \details Certain error-reporting functions may be triggered
393 * before the Logger is initialized or after it is terminated.
394 * This function is meant to help them determine whether the
395 * logger can be used.
396 * \retval true if the Logger can be used
397 * \retval false otherwise
398 */
399 static bool is_initialized();
400
401
402 /**
403 * \brief Creates a division in the log output
404 * \details This is used to start a new "block" of output log with
405 * title \p title. LoggerClients are free to honor div messages or to
406 * implement them the way they prefer.
407 * Example:
408 * \code
409 * Logger::div("new section") << "message" << endl ;
410 * \endcode
411 * \param[in] title title of the division
412 */
413 static std::ostream& div(const std::string& title);
414
415 /**
416 * \brief Gets the stream to send information messages.
417 * \details Example:
418 * \code
419 * Logger::out("feature_name") << "initialized" << endl ;
420 * \endcode
421 * \param[in] feature name of the feature associated to the incoming
422 * information messages
423 * \return a reference to the information stream
424 */
425 static std::ostream& out(const std::string& feature);
426
427 /**
428 * \brief Gets the stream to send error messages
429 * \details Example:
430 * \code
431 * Logger::err("feature_name") << "problem with args" << endl ;
432 * \endcode
433 * \param[in] feature name of the feature associated to the incoming
434 * warning messages
435 * \return a reference to the warning stream
436 */
437 static std::ostream& err(const std::string& feature);
438
439 /**
440 * \brief Gets the stream to send warning messages
441 * \details Example:
442 * \code
443 * Logger::warn("feature_name") << "strange value" << endl ;
444 * \endcode
445 * \param[in] feature name of the feature associated to the incoming
446 * error messages
447 * \return a reference to the error stream
448 */
449 static std::ostream& warn(const std::string& feature);
450
451 /**
452 * \brief Gets the stream to send status messages
453 * \details Example:
454 * \code
455 * Logger::status() << "Hyperdrive activated" << endl ;
456 * \endcode
457 * \return a reference to the status stream
458 */
459 static std::ostream& status();
460
461 /**
462 * \brief Adds a client to the Logger
463 * \details This adds client \p client to the existing clients and
464 * starts sending messages to it. The Logger takes ownership on
465 * the \p client, so there's no need to delete it, unless the client
466 * is unregistered by unregister_client().
467 * \param[in] client a logger client
468 * \see unregister_client()
469 */
470 void register_client(LoggerClient* client);
471
472 /**
473 * \brief Removes a client from the Logger
474 * \details This removes client \p client from the list of registered
475 * clients if present. After being removed, the client will no longer
476 * receive messages from the Logger. It is also the responsibility of
477 * the client code to delete the client appropriately.
478 * \param[in] client a logger client
479 * \see register_client()
480 */
481 void unregister_client(LoggerClient* client);
482
483 /**
484 * \brief Unregisters all the registered clients.
485 */
486 void unregister_all_clients();
487
488 /**
489 * \brief Checks if a client is registered
490 * \param[in] client a logger client
491 * \retval true if \p client is registered to the Logger
492 * \retval false otherwise
493 */
494 bool is_client(LoggerClient* client) const;
495
496 /**
497 * \brief Sets the quiet mode
498 * \details When the Logger is in quiet mode, all messages sent to it
499 * are ignored and not dispatched to the registered clients. The quiet
500 * mode can also be set by setting the value of the property
501 * "log:quiet" with set_value().
502 * \param[in] flag set to true/false to turn the quiet mode on/off
503 * \note The quiet mode is on by default
504 * \see set_value()
505 */
506 void set_quiet(bool flag);
507
508 /**
509 * \brief Checks the quiet mode
510 * \retval true if the quiet mode is on
511 * \retval false otherwise
512 */
513 13253 bool is_quiet() const {
514 13253 return quiet_;
515 }
516
517
518 /**
519 * \brief Sets the minimal mode
520 * \details When the Logger is in minimal mode, only warning and error
521 * messages sent to it are dispatched to the registered clients.
522 * The minimal mode can also be set by setting the value of the property
523 * "log:minimal" with set_value().
524 * \param[in] flag set to true/false to turn the minimal mode on/off
525 * \note The minimal mode is off by default
526 * \see set_value()
527 */
528 void set_minimal(bool flag);
529
530 /**
531 * \brief Checks the minimal mode
532 * \retval true if the minimal mode is on
533 * \retval false otherwise
534 */
535 bool is_minimal() const {
536 return minimal_;
537 }
538
539 /**
540 * \brief Sets the console pretty mode
541 * \details When the Logger console is in pretty mode, messages are
542 * formatted in a fancy way using nice boxes with titles, and long
543 * messages are smartly wrapped and aligned on the message features.
544 * Otherwise, the messages are displayed "as is". The console pretty
545 * mode can also be set by setting the value of the property
546 * "log:pretty" with set_value().
547 * \param[in] flag set to true/false to turn the quiet mode on/off
548 */
549 void set_pretty(bool flag);
550
551 /**
552 * \brief Checks the console pretty mode
553 * \retval true if the console pretty mode is on
554 * \retval false otherwise
555 */
556 109 bool is_pretty() const {
557 109 return pretty_;
558 }
559
560 protected:
561 /**
562 * \brief Logger default constructor
563 * \details The constructor is never called directly but through a call
564 * to initialize().
565 */
566 Logger();
567
568 /**
569 * \brief Logger destructor
570 */
571 ~Logger() override;
572
573 /** \copydoc div() */
574 std::ostream& div_stream(const std::string& title);
575
576 /** \copydoc out() */
577 std::ostream& out_stream(const std::string& feature);
578
579 /** \copydoc err() */
580 std::ostream& err_stream(const std::string& feature);
581
582 /** \copydoc warn() */
583 std::ostream& warn_stream(const std::string& feature);
584
585 /** \copydoc status() */
586 std::ostream& status_stream();
587
588 /**
589 * \brief Gets an output stream that sends messages to the standard
590 * error.
591 * \details This one is returned by out(), err(), warn(), status()
592 * whenever multiple threads are running. It serializes writes
593 * line by line, so that messages from different threads are not mixed.
594 */
595 std::ostream& err_console();
596
597 /**
598 * \brief Receives a message from a logger stream
599 * \details This function is called by the LoggerStream \p stream when
600 * a new sequence of characters \p message is sent to the stream. The
601 * function dispatches the message to appropriate handling functions
602 * notify_xxx() according to the type of the stream
603 * \param[in] sender the LoggerStream that sent the message
604 * \param[in] message the text of the message
605 * \see LoggerStream()
606 * \see notify_out()
607 * \see notify_warn()
608 * \see notify_err()
609 * \see notify_status()
610 */
611 void notify(LoggerStream* sender, const std::string& message);
612
613 /**
614 * \brief Handles an information message
615 * \details This formats the information message and sends it to the
616 * registered clients by calling their function out(). Information
617 * messages are sent to the clients only if the current Logger feature
618 * matches the current filter or does not matches any feature
619 * exclusion rule.
620 * sent to the clients.
621 * \param[in] message text of the message
622 * \see LoggerClient::out()
623 */
624 void notify_out(const std::string& message);
625
626 /**
627 * \brief Handles a warning message
628 * \details This formats the warning message and sends it to the
629 * registered clients by calling their function warn(). Warning
630 * messages ignore feature filters or exclusion rules and are always
631 * sent to the clients.
632 * \param[in] message text of the message
633 * \see LoggerClient::warn()
634 */
635 void notify_warn(const std::string& message);
636
637 /**
638 * \brief Handles an error message
639 * \details This formats the error message and sends it to the
640 * registered clients by calling their function err(). Error
641 * messages ignore feature filters or exclusion rules and are always
642 * sent to the clients.
643 * \param[in] message text of the message
644 * \see LoggerClient::err()
645 */
646 void notify_err(const std::string& message);
647
648 /**
649 * \brief Handles a status message
650 * \details This formats the status message and sends it to the
651 * registered clients by calling their function status(). Status
652 * messages ignore feature filters or exclusion rules and are always
653 * sent to the clients.
654 * \param[in] message text of the message
655 * \see LoggerClient::status()
656 */
657 void notify_status(const std::string& message);
658
659 /**
660 * \brief Sets a Logger property
661 * \details Sets the property \p name with value \p value in the
662 * Logger. The property must be a valid Logger property (see log:xxx
663 * properties in Vorpaline's help) and \p value must be a legal value
664 * for the property.
665 * \param[in] name name of the property
666 * \param[in] value value of the property
667 * \retval true if the property was successfully set
668 * \retval false otherwise
669 * \see Environment::set_value()
670 */
671 bool set_local_value(
672 const std::string& name, const std::string& value
673 ) override;
674
675 /**
676 * \brief Gets a Logger property
677 * \details Retrieves the value of the property \p name and stores it
678 * in \p value. The property must be a valid Logger property (see
679 * log:xxx properties in Vorpaline's help).
680 * \param[in] name name of the property
681 * \param[out] value receives the value of the property
682 * \retval true if the property is a valid Logger property
683 * \retval false otherwise
684 * \see Environment::get_value()
685 */
686 bool get_local_value(
687 const std::string& name, std::string& value
688 ) const override;
689
690
691 /**
692 * \brief Increases number of spaces before each message in out().
693 * \details Used by Stopwatch
694 */
695 456 void indent() {
696 456 ++indent_;
697 456 }
698
699 /**
700 * \brief Decreases number of spaces before each message in out().
701 * \details Used by Stopwatch
702 */
703 456 void unindent() {
704
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 456 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
456 geo_debug_assert(indent_ != 0);
705 456 --indent_;
706 456 }
707
708 private:
709 static SmartPointer<Logger> instance_;
710
711 LoggerStream out_;
712 LoggerStream warn_;
713 LoggerStream err_;
714 LoggerStream status_;
715
716 std::ostream* err_console_;
717
718 // features we want or don't want to log (only applies to 'out').
719
720 /** Set of allowed or excluded features */
721 typedef std::set<std::string> FeatureSet;
722 FeatureSet log_features_;
723 FeatureSet log_features_exclude_;
724 bool log_everything_;
725 std::string log_file_name_;
726
727 std::string current_feature_;
728 bool current_feature_changed_;
729
730 /** Set of registered LoggerClient%s */
731 typedef std::set<LoggerClient_var> LoggerClients;
732 LoggerClients clients_; // list of registered clients
733
734 bool quiet_;
735 bool pretty_;
736 bool minimal_;
737 bool notifying_error_;
738
739 index_t indent_;
740
741 friend class LoggerStream;
742 friend class LoggerStreamBuf;
743 friend class Stopwatch;
744 };
745
746 /************************************************************************/
747
748 }
749
750 extern "C" {
751 /**
752 * \brief Printf-like wrapper to the Logger
753 * \details
754 * By #%defining printf to geogram_printf, legacy code can send printf
755 * formatted messages directly to Logger::out().
756 * \param[in] format printf-like format string
757 * \see printf
758 */
759 int GEOGRAM_API geogram_printf(const char* format, ...);
760
761 /**
762 * \brief Fprintf-like wrapper to the Logger
763 * \details
764 * By #%defining fprintf to geogram_fprintf, legacy code can send fprintf
765 * formatted messages directly to the Logger:
766 * - formatted text printed to stdout is sent to Logger::out()
767 * - formatted text printed to stderr is sent to Logger::err()
768 * - otherwise the formatted text is printed to \p out using
769 * the system fprintf.
770 * \param[in] out output file
771 * \param[in] format printf-like format string
772 * \see fprintf
773 */
774 int GEOGRAM_API geogram_fprintf(FILE* out, const char* format, ...);
775 }
776
777 #else
778
779 #include <stdlib.h>
780
781 #ifndef GEOGRAM_API
782 #define GEOGRAM_API
783 #endif
784
785 /**
786 * \brief Printf-like wrapper to the Logger
787 * \details
788 * By #%defining printf to geogram_printf, legacy code can send printf
789 * formatted messages directly to Logger::out().
790 * \param[in] format printf-like format string
791 * \see printf
792 */
793 extern int GEOGRAM_API geogram_printf(const char* format, ...);
794
795 /**
796 * \brief Fprintf-like wrapper to the Logger
797 * \details
798 * By #%defining fprintf to geogram_fprintf, legacy code can send fprintf
799 * formatted messages directly to the Logger:
800 * - formatted text printed to stdout is sent to Logger::out()
801 * - formatted text printed to stderr is sent to Logger::err()
802 * - otherwise the formatted text is printed to \p out using the system fprintf.
803 * \param[in] out output file
804 * \param[in] format printf-like format string
805 * \see fprintf
806 */
807 extern int GEOGRAM_API geogram_fprintf(FILE* out, const char* format, ...);
808
809 #endif
810
811 #endif
812