print statements

main
Brett 2025-03-04 16:17:16 -05:00
parent 637b4fa0e6
commit 1e30544cff
7 changed files with 291 additions and 60 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 5.1.8) set(BLT_VERSION 5.1.9)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)

View File

@ -35,7 +35,17 @@ namespace blt::logging
DOT, DOT,
MINUS, MINUS,
PLUS, PLUS,
POUND POUND,
LEFT_CHEVRON,
RIGHT_CHEVRON,
CARET
};
enum class fmt_align_t : u8
{
LEFT,
CENTER,
RIGHT
}; };
enum class fmt_sign_t : u8 enum class fmt_sign_t : u8
@ -67,7 +77,8 @@ namespace blt::logging
i64 precision = -1; i64 precision = -1;
fmt_type_t type = fmt_type_t::UNSPECIFIED; fmt_type_t type = fmt_type_t::UNSPECIFIED;
fmt_sign_t sign = fmt_sign_t::MINUS; fmt_sign_t sign = fmt_sign_t::MINUS;
bool leading_zeros = false; fmt_align_t alignment = fmt_align_t::RIGHT;
std::optional<char> prefix_char;
bool uppercase = false; bool uppercase = false;
bool alternate_form = false; bool alternate_form = false;
}; };
@ -100,6 +111,11 @@ namespace blt::logging
public: public:
explicit fmt_parser_t() = default; explicit fmt_parser_t() = default;
fmt_token_t& peek(const size_t offset)
{
return m_tokens[m_pos + offset];
}
fmt_token_t& peek() fmt_token_t& peek()
{ {
return m_tokens[m_pos]; return m_tokens[m_pos];
@ -110,6 +126,11 @@ namespace blt::logging
return m_pos < m_tokens.size(); return m_pos < m_tokens.size();
} }
[[nodiscard]] bool has_next(const size_t offset) const
{
return (m_pos + offset) < m_tokens.size();
}
[[nodiscard]] fmt_token_t& next() [[nodiscard]] fmt_token_t& next()
{ {
return m_tokens[m_pos++]; return m_tokens[m_pos++];
@ -120,18 +141,27 @@ namespace blt::logging
++m_pos; ++m_pos;
} }
void consume(const size_t amount)
{
m_pos += amount;
}
const fmt_spec_t& parse(std::string_view fmt); const fmt_spec_t& parse(std::string_view fmt);
private: private:
static bool is_align_t(fmt_token_type type);
void parse_fmt_field(); void parse_fmt_field();
void parse_arg_id(); void parse_arg_id();
void parse_fmt_spec(); void parse_fmt_spec();
void parse_fmt_spec_align();
void parse_fmt_spec_sign(); void parse_fmt_spec_sign();
void parse_fmt_spec_form(); void parse_fmt_spec_form();
void parse_fmt_spec_width(); void parse_fmt_spec_width();
void parse_fmt_spec_precision(); void parse_fmt_spec_precision();
void parse_align();
void parse_sign(); void parse_sign();
void parse_form(); void parse_form();
void parse_width(); void parse_width();

View File

@ -19,7 +19,6 @@
#ifndef BLT_LOGGING_LOGGING_H #ifndef BLT_LOGGING_LOGGING_H
#define BLT_LOGGING_LOGGING_H #define BLT_LOGGING_LOGGING_H
#include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
@ -37,7 +36,9 @@ namespace blt::logging
struct logger_t struct logger_t
{ {
explicit logger_t() = default; explicit logger_t(std::ostream& stream): m_stream(stream)
{
}
template <typename... Args> template <typename... Args>
std::string log(std::string fmt, Args&&... args) std::string log(std::string fmt, Args&&... args)
@ -51,7 +52,7 @@ namespace blt::logging
return to_string(); return to_string();
} }
std::string to_string(); [[nodiscard]] std::string to_string() const;
private: private:
template <typename... Args, size_t... Indexes> template <typename... Args, size_t... Indexes>
@ -136,7 +137,7 @@ namespace blt::logging
}; };
} }
void setup_stream(const fmt_spec_t& spec); void setup_stream(const fmt_spec_t& spec) const;
void process_strings(); void process_strings();
static void handle_type(std::ostream& stream, const fmt_spec_t& spec); static void handle_type(std::ostream& stream, const fmt_spec_t& spec);
@ -148,7 +149,7 @@ namespace blt::logging
std::optional<std::pair<size_t, size_t>> consume_to_next_fmt(); std::optional<std::pair<size_t, size_t>> consume_to_next_fmt();
std::string m_fmt; std::string m_fmt;
std::stringstream m_stream; std::ostream& m_stream;
fmt_parser_t m_parser; fmt_parser_t m_parser;
// normal sections of string // normal sections of string
std::vector<std::string_view> m_string_sections; std::vector<std::string_view> m_string_sections;
@ -159,7 +160,7 @@ namespace blt::logging
size_t m_arg_pos = 0; size_t m_arg_pos = 0;
}; };
void print(const std::string& fmt); void print(const std::string& str);
void newline(); void newline();
@ -172,12 +173,26 @@ namespace blt::logging
print(logger.log(std::move(fmt), std::forward<Args>(args)...)); print(logger.log(std::move(fmt), std::forward<Args>(args)...));
} }
template <typename... Args>
void print(std::ostream& stream, std::string fmt, Args&&... args)
{
auto& logger = get_global_logger();
stream << logger.log(std::move(fmt), std::forward<Args>(args)...);
}
template <typename... Args> template <typename... Args>
void println(std::string fmt, Args&&... args) void println(std::string fmt, Args&&... args)
{ {
print(std::move(fmt), std::forward<Args>(args)...); print(std::move(fmt), std::forward<Args>(args)...);
newline(); newline();
} }
template <typename... Args>
void println(std::ostream& stream, std::string fmt, Args&&... args)
{
print(stream, std::move(fmt), std::forward<Args>(args)...);
stream << std::endl;
}
} }
#endif // BLT_LOGGING_LOGGING_H #endif // BLT_LOGGING_LOGGING_H

View File

@ -19,6 +19,8 @@
#ifndef BLT_META_IS_STREAMABLE_H #ifndef BLT_META_IS_STREAMABLE_H
#define BLT_META_IS_STREAMABLE_H #define BLT_META_IS_STREAMABLE_H
#include <ostream>
namespace blt::meta namespace blt::meta
{ {
// https://stackoverflow.com/questions/66397071/is-it-possible-to-check-if-overloaded-operator-for-type-or-class-exists // https://stackoverflow.com/questions/66397071/is-it-possible-to-check-if-overloaded-operator-for-type-or-class-exists

View File

@ -49,6 +49,12 @@ namespace blt::logging
return fmt_token_type::SPACE; return fmt_token_type::SPACE;
case '#': case '#':
return fmt_token_type::POUND; return fmt_token_type::POUND;
case '<':
return fmt_token_type::LEFT_CHEVRON;
case '>':
return fmt_token_type::RIGHT_CHEVRON;
case '^':
return fmt_token_type::CARET;
default: default:
return fmt_token_type::STRING; return fmt_token_type::STRING;
} }
@ -66,6 +72,9 @@ namespace blt::logging
case fmt_token_type::DOT: case fmt_token_type::DOT:
case fmt_token_type::COLON: case fmt_token_type::COLON:
case fmt_token_type::POUND: case fmt_token_type::POUND:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CARET:
return fmt_token_t{base_type, std::string_view{m_fmt.data() + m_pos++, 1}}; return fmt_token_t{base_type, std::string_view{m_fmt.data() + m_pos++, 1}};
default: default:
{ {
@ -99,6 +108,19 @@ namespace blt::logging
return m_spec; return m_spec;
} }
bool fmt_parser_t::is_align_t(const fmt_token_type type)
{
switch (type)
{
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::CARET:
return true;
default:
return false;
}
}
void fmt_parser_t::parse_fmt_field() void fmt_parser_t::parse_fmt_field()
{ {
if (!has_next()) if (!has_next())
@ -119,12 +141,7 @@ namespace blt::logging
case fmt_token_type::COLON: case fmt_token_type::COLON:
parse_fmt_spec(); parse_fmt_spec();
break; break;
case fmt_token_type::STRING: default:
case fmt_token_type::SPACE:
case fmt_token_type::DOT:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
case fmt_token_type::POUND:
{ {
std::stringstream ss; std::stringstream ss;
ss << "Expected unknown token '" << static_cast<u8>(type) << "' value '" << value << "' when parsing format field"; ss << "Expected unknown token '" << static_cast<u8>(type) << "' value '" << value << "' when parsing format field";
@ -155,7 +172,18 @@ namespace blt::logging
switch (type) switch (type)
{ {
case fmt_token_type::STRING: case fmt_token_type::STRING:
if (has_next(1))
{
const auto [next_type, next_value] = peek(1);
if (is_align_t(next_type))
parse_fmt_spec_align();
}
return; return;
case fmt_token_type::RIGHT_CHEVRON:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::CARET:
parse_fmt_spec_align();
break;
case fmt_token_type::COLON: case fmt_token_type::COLON:
{ {
std::stringstream ss; std::stringstream ss;
@ -179,6 +207,42 @@ namespace blt::logging
} }
} }
void fmt_parser_t::parse_fmt_spec_align()
{
parse_align();
if (!has_next())
return;
auto [type, value] = peek();
switch (type)
{
case fmt_token_type::STRING:
return;
case fmt_token_type::NUMBER:
parse_fmt_spec_width();
break;
case fmt_token_type::DOT:
parse_fmt_spec_precision();
break;
case fmt_token_type::SPACE:
case fmt_token_type::MINUS:
case fmt_token_type::PLUS:
parse_fmt_spec_sign();
break;
case fmt_token_type::POUND:
parse_fmt_spec_form();
break;
case fmt_token_type::CARET:
case fmt_token_type::COLON:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
{
std::stringstream ss;
ss << "(Stage (Begin)) Invalid token type " << static_cast<u8>(type) << " value " << value;
throw std::runtime_error(ss.str());
}
}
}
// handle start of fmt, with sign // handle start of fmt, with sign
void fmt_parser_t::parse_fmt_spec_sign() void fmt_parser_t::parse_fmt_spec_sign()
{ {
@ -194,6 +258,9 @@ namespace blt::logging
case fmt_token_type::MINUS: case fmt_token_type::MINUS:
case fmt_token_type::PLUS: case fmt_token_type::PLUS:
case fmt_token_type::COLON: case fmt_token_type::COLON:
case fmt_token_type::CARET:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
{ {
std::stringstream ss; std::stringstream ss;
ss << "(Stage (Sign)) Invalid token type " << static_cast<u8>(type) << " value " << value; ss << "(Stage (Sign)) Invalid token type " << static_cast<u8>(type) << " value " << value;
@ -226,6 +293,9 @@ namespace blt::logging
case fmt_token_type::MINUS: case fmt_token_type::MINUS:
case fmt_token_type::PLUS: case fmt_token_type::PLUS:
case fmt_token_type::POUND: case fmt_token_type::POUND:
case fmt_token_type::CARET:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
{ {
std::stringstream ss; std::stringstream ss;
ss << "(Stage (Form)) Invalid token type " << static_cast<u8>(type) << " value " << value; ss << "(Stage (Form)) Invalid token type " << static_cast<u8>(type) << " value " << value;
@ -257,6 +327,9 @@ namespace blt::logging
case fmt_token_type::SPACE: case fmt_token_type::SPACE:
case fmt_token_type::POUND: case fmt_token_type::POUND:
case fmt_token_type::NUMBER: case fmt_token_type::NUMBER:
case fmt_token_type::CARET:
case fmt_token_type::LEFT_CHEVRON:
case fmt_token_type::RIGHT_CHEVRON:
{ {
std::stringstream ss; std::stringstream ss;
ss << "(Stage (Width)) Invalid token type " << static_cast<u8>(type) << " value " << value; ss << "(Stage (Width)) Invalid token type " << static_cast<u8>(type) << " value " << value;
@ -275,6 +348,36 @@ namespace blt::logging
parse_precision(); parse_precision();
} }
void fmt_parser_t::parse_align()
{
auto [type, value] = next();
fmt_token_type process_type = type;
if (type == fmt_token_type::STRING)
{
auto [next_type, next_value] = next();
process_type = next_type;
m_spec.prefix_char = value.front();
}
switch (process_type)
{
case fmt_token_type::LEFT_CHEVRON:
m_spec.alignment = fmt_align_t::LEFT;
break;
case fmt_token_type::RIGHT_CHEVRON:
m_spec.alignment = fmt_align_t::RIGHT;
break;
case fmt_token_type::CARET:
m_spec.alignment = fmt_align_t::CENTER;
break;
default:
{
std::stringstream ss;
ss << "Invalid align type " << static_cast<u8>(process_type) << " value " << value;
throw std::runtime_error(ss.str());
}
}
}
void fmt_parser_t::parse_sign() void fmt_parser_t::parse_sign()
{ {
auto [_, value] = next(); auto [_, value] = next();
@ -313,8 +416,8 @@ namespace blt::logging
void fmt_parser_t::parse_width() void fmt_parser_t::parse_width()
{ {
auto [_, value] = next(); auto [_, value] = next();
if (value.front() == '0') if (value.front() == '0' && !m_spec.prefix_char.has_value())
m_spec.leading_zeros = true; m_spec.prefix_char = '0';
m_spec.width = std::stoll(std::string(value)); m_spec.width = std::stoll(std::string(value));
} }

View File

@ -17,6 +17,7 @@
*/ */
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream>
#include <blt/iterator/enumerate.h> #include <blt/iterator/enumerate.h>
#include <blt/logging/logging.h> #include <blt/logging/logging.h>
#include <blt/std/types.h> #include <blt/std/types.h>
@ -25,23 +26,33 @@ namespace blt::logging
{ {
struct logging_thread_context_t struct logging_thread_context_t
{ {
logger_t logger; std::stringstream stream;
logger_t logger{stream};
}; };
std::string logger_t::to_string() std::string logger_t::to_string() const
{ {
auto str = m_stream.str(); return dynamic_cast<std::stringstream&>(m_stream).str();
m_stream.str("");
m_stream.clear();
return str;
} }
void logger_t::setup_stream(const fmt_spec_t& spec) void logger_t::setup_stream(const fmt_spec_t& spec) const
{ {
if (spec.leading_zeros) if (spec.prefix_char)
m_stream << std::setfill('0'); m_stream << std::setfill(*spec.prefix_char);
else else
m_stream << std::setfill(' '); m_stream << std::setfill(' ');
switch (spec.alignment)
{
case fmt_align_t::LEFT:
m_stream << std::left;
break;
case fmt_align_t::CENTER:
// TODO?
break;
case fmt_align_t::RIGHT:
m_stream << std::right;
break;
}
if (spec.width > 0) if (spec.width > 0)
m_stream << std::setw(static_cast<i32>(spec.width)); m_stream << std::setw(static_cast<i32>(spec.width));
else else
@ -52,6 +63,8 @@ namespace blt::logging
m_stream << std::setprecision(16); m_stream << std::setprecision(16);
if (spec.alternate_form) if (spec.alternate_form)
m_stream << std::showbase; m_stream << std::showbase;
else
m_stream << std::noshowbase;
if (spec.uppercase) if (spec.uppercase)
m_stream << std::uppercase; m_stream << std::uppercase;
else else
@ -129,7 +142,8 @@ namespace blt::logging
m_fmt = std::move(fmt); m_fmt = std::move(fmt);
m_last_fmt_pos = 0; m_last_fmt_pos = 0;
m_arg_pos = 0; m_arg_pos = 0;
m_stream.str(""); auto& ss = dynamic_cast<std::stringstream&>(m_stream);
ss.str("");
m_stream.clear(); m_stream.clear();
m_string_sections.clear(); m_string_sections.clear();
m_fmt_specs.clear(); m_fmt_specs.clear();
@ -171,9 +185,9 @@ namespace blt::logging
return context.logger; return context.logger;
} }
void print(const std::string& fmt) void print(const std::string& str)
{ {
std::cout << fmt; std::cout << str;
} }
void newline() void newline()

View File

@ -15,45 +15,112 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <iostream>
#include <sstream>
#include <blt/logging/logging.h> #include <blt/logging/logging.h>
#include <blt/std/assert.h>
#include <blt/std/utility.h> #include <blt/std/utility.h>
struct some_silly_type_t struct some_silly_type_t
{ {
}; };
auto expected_str = std::string(R"(This is a println!
This is a println with args '42'
This is a println with multiple args '42' '32.342311859130859375' 'Hello World!'
This is a 'Well so am I except cooler :3' fmt string with positionals 'I am a string!'
This is a println with a sign +4120
This is a println with a sign -4120
This is a println with a space 4120
This is a println with a space -4120
This is a println with a minus 4120
This is a println with a minus -4120
This is a println with a with 4120
This is a println with a with leading zeros 0000004120
This is a println with a precision 42.2323423490
This is a println with hex 109a
This is a println with hex with leading 0x109a
This is a println with binary 0b00110010000110100101011000000000
This is a println with binary with space 0b10110010 00011010 01010110 00000000
This is a println with binary with space 10100010 00000000 00000000 00000000
This is a println with octal 015015
This is a println with hexfloat 0x1.926e978d4fdf4p+8
This is a println with exponent 4.4320902431999996e+07
This is a println with exponent 9.5324342340423400e+15
This is a println with general 953243.49
This is a println with general 9.532433240234033e+17
This is a println with a char B
This is a println with type some_silly_type_t
This is a println with boolean true
This is a println with boolean as int 0
This is a println with boolean as hex 0x1
This is a println with boolean as octal 1
This is a println with alignment left 64 end value
This is a println with alignment right 46 end value
This is a println with alignment left (fill) 46******** end value
This is a println with alignment right (fill) ********46 end value
)");
std::pair<bool, std::string> compare_strings(const std::string& s1, const std::string& s2)
{
if (s1.size() != s2.size())
return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"};
size_t index = 0;
for (; index < s1.size(); ++index)
{
if (s1[index] != s2[index])
{
std::stringstream ss;
const auto i1 = std::max(static_cast<blt::i64>(index) - 32, 0l);
const auto l1 = std::min(static_cast<blt::i64>(s1.size()) - i1, 65l);
ss << "Strings differ at index " << index << "!\n";
ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl;
return {false, ss.str()};
}
}
return {true, ""};
}
int main() int main()
{ {
blt::logging::println("This is a println!"); std::stringstream ss;
blt::logging::println("This is a println with args '{}'", 42); blt::logging::println(ss, "This is a println!");
blt::logging::println("This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!"); blt::logging::println(ss, "This is a println with args '{}'", 42);
blt::logging::println("This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3"); blt::logging::println(ss, "This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!");
blt::logging::println("This is a println with a sign {:+}", 4120); blt::logging::println(ss, "This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3");
blt::logging::println("This is a println with a sign {:+}", -4120); blt::logging::println(ss, "This is a println with a sign {:+}", 4120);
blt::logging::println("This is a println with a space {: }", 4120); blt::logging::println(ss, "This is a println with a sign {:+}", -4120);
blt::logging::println("This is a println with a space {: }", -4120); blt::logging::println(ss, "This is a println with a space {: }", 4120);
blt::logging::println("This is a println with a minus {:-}", 4120); blt::logging::println(ss, "This is a println with a space {: }", -4120);
blt::logging::println("This is a println with a minus {:-}", -4120); blt::logging::println(ss, "This is a println with a minus {:-}", 4120);
blt::logging::println("This is a println with a with {:10}", 4120); blt::logging::println(ss, "This is a println with a minus {:-}", -4120);
blt::logging::println("This is a println with a with leading zeros {:010}", 4120); blt::logging::println(ss, "This is a println with a with {:10}", 4120);
blt::logging::println("This is a println with a precision {:.10f}", 42.232342349); blt::logging::println(ss, "This is a println with a with leading zeros {:010}", 4120);
blt::logging::println("This is a println with hex {:.10x}", 4250); blt::logging::println(ss, "This is a println with a precision {:.10f}", 42.232342349);
blt::logging::println("This is a println with hex with leading {:#.10x}", 4250); blt::logging::println(ss, "This is a println with hex {:.10x}", 4250);
blt::logging::println("This is a println with binary {:#b}", 6969420); blt::logging::println(ss, "This is a println with hex with leading {:#.10x}", 4250);
blt::logging::println("This is a println with binary with space {: #b}", 6969421); blt::logging::println(ss, "This is a println with binary {:#b}", 6969420);
blt::logging::println("This is a println with binary with space {: b}", 69); blt::logging::println(ss, "This is a println with binary with space {: #b}", 6969421);
blt::logging::println("This is a println with octal {:#o}", 6669); blt::logging::println(ss, "This is a println with binary with space {: b}", 69);
blt::logging::println("This is a println with hexfloat {:a}", 402.4320); blt::logging::println(ss, "This is a println with octal {:#o}", 6669);
blt::logging::println("This is a println with exponent {:e}", 44320902.4320); blt::logging::println(ss, "This is a println with hexfloat {:a}", 402.4320);
blt::logging::println("This is a println with exponent {:e}", 9532434234042340.0); blt::logging::println(ss, "This is a println with exponent {:e}", 44320902.4320);
blt::logging::println("This is a println with general {:g}", 953243.49); blt::logging::println(ss, "This is a println with exponent {:e}", 9532434234042340.0);
blt::logging::println("This is a println with general {:g}", 953243324023403240.49); blt::logging::println(ss, "This is a println with general {:g}", 953243.49);
blt::logging::println("This is a println with a char {:c}", 66); blt::logging::println(ss, "This is a println with general {:g}", 953243324023403240.49);
blt::logging::println("This is a println with type {:t}", some_silly_type_t{}); blt::logging::println(ss, "This is a println with a char {:c}", 66);
blt::logging::println("This is a println with boolean {}", true); blt::logging::println(ss, "This is a println with type {:t}", some_silly_type_t{});
blt::logging::println("This is a println with boolean as int {:d}", false); blt::logging::println(ss, "This is a println with boolean {}", true);
blt::logging::println("This is a println with boolean as hex {:#x}", true); blt::logging::println(ss, "This is a println with boolean as int {:d}", false);
blt::logging::println("This is a println with boolean as octal {:o}", true); blt::logging::println(ss, "This is a println with boolean as hex {:#x}", true);
blt::logging::println("This is a println with boolean as test {:h}", true); blt::logging::println(ss, "This is a println with boolean as octal {:o}", true);
blt::logging::println(ss, "This is a println with alignment left {:<10} end value", 64);
blt::logging::println(ss, "This is a println with alignment right {:>10} end value", 46);
blt::logging::println(ss, "This is a println with alignment left (fill) {:*<10} end value", 46);
blt::logging::println(ss, "This is a println with alignment right (fill) {:*>10} end value", 46);
blt::logging::print(ss.str());
auto [passed, error_msg] = compare_strings(expected_str, ss.str());
BLT_ASSERT_MSG(passed && "Logger logged string doesn't match precomputed expected string!", error_msg.c_str());
// blt::logging::println("This is println {}\twith a std::endl in the middle of it"); // blt::logging::println("This is println {}\twith a std::endl in the middle of it");
} }