C++17 Features Used in plog

C++17 Features Used in plog

plog is a relatively lightweight logging library primarily based on C11/14, which **does not heavily rely on C17**, but it does support and utilize some C++17 features.

1. Inline Variables

The inline variable feature introduced in C++17 allows the definition of global variables in header files without violating the ODR (One Definition Rule).

// plog/include/plog/Init.h or similar file
namespace plog {
    // C++17 inline variable for singleton pattern
    inline constexpr const char* kDefaultLoggerName = "default";
    
    // Or for static member variables
    template<int instanceId>
    class LoggerStorage {
        inline static Logger<instanceId>* s_instance = nullptr;
    };
}

Use Case: Avoids multiple definitions of variables across translation units, particularly suitable for header-only libraries.

2. constexpr if (Compile-time Conditional Statements)

// plog/include/plog/Record.h
template<typename T>
void writeToStream(std::ostream& os, const T& value) {
    if constexpr (std::is_arithmetic_v<T>) {
        // Special handling for arithmetic types
        os << value;
    }
    else if constexpr (std::is_convertible_v<T, std::string>) {
        // For types convertible to string
        os << static_cast<std::string>(value);
    }
    else {
        // Default handling
        os << value;
    }
}

Advantages: Compile-time branching, no runtime overhead, and branches that do not meet the condition are not instantiated.

3. Structured Bindings

Although not commonly used internally in plog, it may appear in example code:

// Example: Code using plog
#include <map>
#include <plog/Log.h>

void processConfig() {
    std::map<std::string, int> config;
    
    // C++17 structured bindings
    auto [iter, inserted] = config.insert({"maxSize", 1024});
    if (inserted) {
        PLOG_INFO << "Config inserted: " << iter->first;
    }
}

4. std::string_view Support

// plog/include/plog/Util.h
namespace plog {
namespace util {
    
    // Accepts string_view parameters to avoid unnecessary string copies
    inline std::string formatMessage(std::string_view message, 
                                     std::string_view file, 
                                     int line) {
        std::ostringstream ss;
        ss << "[" << file << ":" << line << "] " << message;
        return ss.str();
    }
    
    // Or in Formatter
    class TxtFormatter {
    public:
        static std::string format(std::string_view msg, 
                                 std::string_view file) {
            return std::string(file) + ": " + std::string(msg);
        }
    };
}
}

Advantages: Zero-copy string views improve performance.

5. Fold Expressions

Simplification for variadic templates:

// plog/include/plog/Record.h
template<typename... Args>
class Record {
    template<typename... Ts>
    Record& operator<<(const std::tuple<Ts...>& values) {
        // C++17 fold expression
        ((m_stream << std::get<Ts>(values) << " "), ...);
        return *this;
    }
    
private:
    std::ostringstream m_stream;
};

6. Class Template Argument Deduction (CTAD)

// Simplified usage when using plog
#include <plog/Appenders/RollingFileAppender.h>
#include <plog/Formatters/TxtFormatter.h>

// Before C++17
plog::RollingFileAppender<plog::TxtFormatter> appender("log.txt");

// C++17 allows omission of template parameters (if suitable deduction guides exist)
// plog::RollingFileAppender appender("log.txt", plog::TxtFormatter{});

7. [[nodiscard]] Attribute

Prevents ignoring important return values:

// plog/include/plog/Logger.h
namespace plog {
    class Logger {
    public:
        [[nodiscard]] bool checkSeverity(Severity severity) const {
            return severity <= m_maxSeverity;
        }
        
        [[nodiscard]] Record& operator<<(Severity severity) {
            // ...
            return createRecord(severity);
        }
    };
}

8. [[maybe_unused]] Attribute

Used for conditionally compiled code:

// plog/include/plog/Util.h
namespace plog {
    namespace util {
        inline void debugPrint([[maybe_unused]] const char* msg) {
#ifdef PLOG_ENABLE_DEBUG
            std::cout << msg << std::endl;
#endif
        }
    }
}

9. Nested Namespace Definitions

// Before C++17
namespace plog {
    namespace formatters {
        namespace impl {
            // ...
        }
    }
}

// C++17 simplified syntax
namespace plog::formatters::impl {
    class FormatterBase {
        // ...
    };
}

10. char8_t Support (UTF-8 Character Type)

This feature is explicitly demonstrated in the plog demo:

// From samples/Demo/Main.cpp
#ifdef __cpp_char8_t
    PLOG_DEBUG << u8"Chinese: 中文";
    PLOG_DEBUG << const_cast<const char8_t*>(u8"Cyrillic: тест");
#endif

11. std::filesystem (Optional Support)

Although not a core feature, it can be used for log file management:

// plog/include/plog/Appenders/RollingFileAppender.h
#if __cplusplus >= 201703L
#include <filesystem>

namespace plog {
    class RollingFileAppender {
        void rollFiles() {
            namespace fs = std::filesystem;
            
            if (fs::exists(m_fileName) && 
                fs::file_size(m_fileName) > m_maxSize) {
                // Roll log files
                for (int i = m_maxFiles - 1; i > 0; --i) {
                    fs::path src = m_fileName + "." + std::to_string(i);
                    fs::path dst = m_fileName + "." + std::to_string(i + 1);
                    if (fs::exists(src)) {
                        fs::rename(src, dst);
                    }
                }
            }
        }
    };
}
#endif

Conclusion

plog is primarily a C++11/14 compatible library, but it:

  1. 1. Conditionally uses C++17 features (detected via the <span>__cplusplus</span> macro)
  2. 2. Does not enforce C++17, maintaining backward compatibility
  3. 3. Utilizes C++17 features when available to enhance performance and usability

The most commonly used C++17 features include:

  • <span>inline</span> variables (key for header-only libraries)
  • <span>constexpr if</span> (type checking)
  • <span>std::string_view</span> (performance optimization)
  • <span>[[nodiscard]]</span> / <span>[[maybe_unused]]</span> (code quality)
  • • UTF-8 literal support (internationalization)

This design allows plog to work on older compilers while enjoying the advantages of modern C++ features on newer compilers.

Leave a Comment