I have been experimenting with C++ 20 modules.
ZIP file of my C++ 20 modules experiment.
I converted my logging routine over to a module. Preprocessor macros for source file information were replaced with std::source_location. Output streams are now passed as template arguments to enable normal messages to pass to std::cout and errors to std::cerr.
This module was compiled using:
The module is named in Java like syntax using my URL to make a unique module name. Due to the lack of availability of a modularized standard library, I've included the headers rather than imports.
I find C++ 20 modules very interesting and will revisit them as they mature.
This is the output of my sample program.
INFO:/home/steve/Projects/modules/main.cpp:11:int main():start
DEBUG:/home/steve/Projects/modules/main.cpp:13:int main():hello
DEBUG:/home/steve/Projects/modules/main.cpp:13:int main():world
INFO:/home/steve/Projects/modules/main.cpp:14:int main():try
WARN:/home/steve/Projects/modules/main.cpp:15:int main():about to throw
THROW:/home/steve/Projects/modules/main.cpp:16:int main():throw fit
INFO:/home/steve/Projects/modules/main.cpp:21:int main():end
ERROR:/home/steve/Projects/modules/main.cpp:19:int main():caught an error
Process finished with exit code 0
The source files follow.
#include <iostream>
import com.threedbb.ulog;
#define LDEBUG LDebug()
#define LINFO LInfo()
#define LWARN LWarn()
#define LERROR LError()
int main() {
LINFO << "start";
try {
LDEBUG << "hello\nworld\n";
LINFO << "try";
LWARN << "about to throw";
LTHROW("throw fit");
} catch (std::exception &e) {
std::cout << e.what() << "\n";
LERROR << "caught an error";
}
LINFO << "end\n";
return 0;
}
module;
#include <algorithm>
#include <iostream>
#include <limits>
#include <locale>
#include <ranges>
#include <source_location>
#include <sstream>
#include <string>
#include <vector>
export module com.threedbb.ulog;
template<size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
struct Ostream {
constexpr Ostream(std::ostream &os) : value(os) {}
std::ostream &value;
};
template<::StringLiteral LIT, Ostream OUT>
class ULog;
export std::string toUTF8(const std::u16string &str);
export using LDebug = ULog<"DEBUG", std::cout>;
export using LInfo = ULog<"INFO", std::cout>;
export using LWarn = ULog<"WARN", std::cout>;
export using LError = ULog<"ERROR", std::cerr>;
export void LTHROW(
const std::string &msg,
const std::source_location &loc = std::source_location::current());
std::string asLogFormat(
const std::string &level,
const std::string &msg,
const std::source_location &loc);
template<StringLiteral LIT, Ostream OUT>
class ULog {
private:
std::ostream &output = OUT.value;
std::string m_logLevel = LIT.value;
std::source_location m_loc;
std::stringstream m_ss;
public:
explicit ULog(const std::source_location &loc =
std::source_location::current()) : m_loc(loc) {}
~ULog() {
std::string msg;
while (std::getline(m_ss, msg, '\n')) {
output << asLogFormat(m_logLevel, msg, m_loc) << "\n";
}
}
template<typename T>
ULog &operator<<(T const &Xi_val) {
m_ss << Xi_val;
return *this;
}
ULog &operator<<(const std::u16string &u16) {
m_ss << toUTF8(u16);
return *this;
}
ULog &operator<<(std::ostream &(*f)(std::ostream &)) {
if (f == static_cast<decltype(f)>(std::endl)) {
m_ss << '\n';
}
return *this;
}
std::ostream &os() {
return m_ss;
}
};
module;
#include <exception>
#include <algorithm>
#include <codecvt>
#include <locale>
#include <ranges>
#include <source_location>
#include <string>
#include <vector>
#include <fmt/core.h>
module com.threedbb.ulog;
std::string toUTF8(const std::u16string &str) {
std::wstring_convert<
std::codecvt_utf8_utf16<char16_t>,
char16_t> convert;
return convert.to_bytes(str);
}
void LTHROW(const std::string &msg, const std::source_location &loc) {
throw std::runtime_error(asLogFormat("THROW",msg,loc));
}
std::string asLogFormat(
const std::string &level,
const std::string &msg,
const std::source_location &loc) {
return fmt::format("{}:{}:{}:{}:{}",
level,
loc.file_name(),
loc.line(),
loc.function_name(),
msg);
}
cmake_minimum_required(VERSION 3.28)
project(modules)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
add_compile_options(-fexperimental-library)
link_libraries(fmt)
add_library(mylog com.threedbb.ulog.impl.cpp)
target_sources(mylog
PUBLIC
FILE_SET CXX_MODULES FILES
com.threedbb.ulog.ixx)
add_executable(modules main.cpp)
target_link_libraries(modules mylog)