Open TaciturnJian opened 2 months ago
#include <fmt/core.h>
#include <fmt/format.h>
#include <iostream>
#include <string_view>
class Data {
public:
int val1;
int val2;
Data(int v1, int v2) : val1(v1), val2(v2) {}
};
// 载 fmt::formatter<Data> 以支持 fmt::print 对 Data 类的输出
template <>
struct fmt::formatter<Data> {
constexpr auto parse(format_parse_context& ctx) {
// 解析{:1}, {:2}, {:3}
auto begin = ctx.begin(), end = ctx.end();
for (auto it = begin; it != end; ++it) {
if (*it == '}') return it;
if (*it == '{') throw format_error("invalid format");
switch (*it) {
case '1':
m_outputType = OutputType::Val1;
break;
case '2':
m_outputType = OutputType::Val2;
break;
case '3':
m_outputType = OutputType::Val12;
break;
default:
//TODO: test_format_custom.cc:55:41: error: expression ‘<throw-expression>’ is not a constant expression
throw fmt::format_error {"Invalid kv format specifier."};
}
}
}
template <typename Context>
auto format(const Data& myclass, Context& ctx) {
// 根据解析的格式字符串进行格式化
switch (m_outputType) {
case OutputType::Val1:
return fmt::format_to(ctx.out(), "{}", myclass.val1);
case OutputType::Val2:
return fmt::format_to(ctx.out(), "{}", myclass.val2);
default:
return fmt::format_to(ctx.out(), "{}, {}", myclass.val1, myclass.val2);
}
}
private:
enum class OutputType
{
Val1,
Val2,
Val12
};
OutputType m_outputType { OutputType::Val12 };
};
int main() {
Data d1(1, 2);
fmt::print("The contents of d1 are: {:3}\n", d1);
fmt::print("The value of val1 is: {:1}\n", d1);
fmt::print("The value of val2 is: {:2}\n", d1);
}
先存着可以打印字符串的 `
struct MyStruct { int data1; double data2; std::string data3; };
namespace fmt {
template <>
struct formatter<MyStruct> {
// 用于存储格式说明符
char spec = '\0';
constexpr auto parse(format_parse_context& ctx) {
auto it = ctx.begin();
auto end = ctx.end();
// 读取格式说明符
if (it != end && *it != '}') {
spec = *it++;
}
// 返回解析后的迭代器位置
return it;
}
template <typename FormatContext>
auto format(const MyStruct& s, FormatContext& ctx) {
switch (spec) {
case 'a': // 输出 data1
return format_to(ctx.out(), "data1: {}", s.data1);
case 'b': // 输出 data2
return format_to(ctx.out(), "data2: {}", s.data2);
default: // 默认输出剩余的数据
return format_to(ctx.out(), "data3: {}", s.data3);
}
}
};
}
int main() { MyStruct s{ 42, 3.14, "Hello, world!" }; MyStruct s2{ 2, 3, "Hello" }; fmt::print("Formatted output (a): {:a}\n", s); // 输出 data1 fmt::print("Formatted output (b): {:b}\n", s); // 输出 data2 fmt::print("Formatted output (default): {1} {0}\n", s, s2); // 默认输出 data3 return 0; }
`
这里又编写了一个可以自由打印单类但是一次性打印vector结构体有bug的结构化示例: 头文件:
#pragma once
#include <concepts>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <span>
#include <string>
#include <sstream>
namespace Cango::inline TaskDesign {
/// @brief Concept for objects that can be formatted using fmt.
template <typename TObject>
concept IsFormattableObject = requires(const TObject & obj, fmt::memory_buffer & buffer) {
{ obj.Format(buffer) } -> std::convertible_to<void>;
};
/// @brief Base class for formattable objects, providing a Format method that formats the object to a fmt buffer.
struct FormattableObject {
/// @brief Formats the object into the provided fmt buffer.
/// @param buffer The fmt memory buffer for formatting.
/// @note Override this method to customize the formatting of derived objects.
virtual void Format(fmt::memory_buffer& buffer) const noexcept = 0;
/// @brief Converts the object to a string using the Format method.
std::string ToString() const noexcept {
fmt::memory_buffer buffer;
Format(buffer);
return fmt::to_string(buffer);
}
/// @brief Outputs the object to a stream using fmt.
friend std::ostream& operator<<(std::ostream& os, const FormattableObject& obj) {
return os << obj.ToString();
}
};
/// @brief Formats a collection of formattable objects as a list.
/// @param buffer The fmt memory buffer for formatting.
/// @param objects A span of formattable objects to be formatted.
template <IsFormattableObject TObject>
void Format(fmt::memory_buffer& buffer, const std::span<const TObject> objects) noexcept {
const auto size = objects.size();
if (size == 0) {
fmt::format_to(buffer, "[]");
return;
}
fmt::format_to(buffer, "[");
for (size_t i = 0; i < size; ++i) {
objects[i].Format(buffer);
if (i < size - 1) {
fmt::format_to(buffer, ", ");
}
}
fmt::format_to(buffer, "]");
}
/// @brief Outputs a collection of formattable objects as a formatted list to a stream.
template <IsFormattableObject TObject>
std::ostream& operator<<(std::ostream& stream, const std::span<const TObject> objects) noexcept {
fmt::memory_buffer buffer;
Format(buffer, objects);
return stream << fmt::to_string(buffer);
}
/// @brief Converts an object to a string using the Format method.
std::string Format(const IsFormattableObject auto& obj) noexcept {
return obj.ToString();
}
}
cpp文件:
#include <FormatObject.h>
#include <vector>
// Example of a struct inheriting from FormattableObject
struct MyStruct : public Cango::TaskDesign::FormattableObject {
int data1;
double data2;
std::string data3;
// Override the Format method
void Format(fmt::memory_buffer& buffer) const noexcept override {
fmt::format_to(std::back_inserter(buffer), "{{data1: {}, data2: {:.2f}, data3: \"{}\"}}", data1, data2, data3);
}
// Add a constructor
MyStruct(int d1, double d2, const std::string& d3) : data1(d1), data2(d2), data3(d3) {}
};
//namespace fmt {
// template<>
// struct fmt::formatter<MyStruct> {
// template <typename ParseContext>
// constexpr auto parse(ParseContext& ctx) { return ctx.begin(); }
//
// template <typename FormatContext>
// auto format(const MyStruct& s, FormatContext& ctx) {
// return fmt::format_to(ctx.out(), "{}", s.ToString());
// }
// };
//}
// Another example struct to test collections
struct AnotherStruct : public Cango::TaskDesign::FormattableObject {
std::string name;
int value;
// Override the Format method
void Format(fmt::memory_buffer& buffer) const noexcept override {
fmt::format_to(std::back_inserter(buffer), "{{name: \"{}\", value: {}}}", name, value);
}
// Add a constructor
AnotherStruct(const std::string& n, int v) : name(n), value(v) {}
};
//这里似乎是在重复啰嗦,但我不知道为什么头文件里定义的那个没什么用
template <typename T>
requires Cango::TaskDesign::IsFormattableObject<T>
struct fmt::formatter<T> {
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) { return ctx.begin(); };
template <typename FormatContext>
auto format(const T& obj, FormatContext& ctx) {
fmt::memory_buffer buffer;
obj.Format(buffer);
return fmt::format_to(ctx.out(), "{}", fmt::to_string(buffer));
}
};
int main() {
// Test single object formatting
MyStruct s{ 42, 3.14159, "Hello, world!" };
fmt::print("Single object: {}\n", s);
// Test formatting with multiple objects
AnotherStruct a1{ "Object1", 10 };
AnotherStruct a2{ "Object2", 20 };
AnotherStruct a3{ "Object3", 30 };
std::vector<AnotherStruct> objects = { a1, a2, a3 };
std::span<const AnotherStruct> objectSpan{ objects.data(), objects.size() };
fmt::print("Multiple objects: {0} {2} {1}\n", a1, a2 ,a3);
// Test the Format function returning a string
std::string formattedString = Cango::TaskDesign::Format(s);
fmt::print("Formatted string: {}\n", formattedString);
return 0;
}
variant操作: 头文件
#pragma once
#include <concepts>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <span>
#include <string>
#include <sstream>
#include <variant>
namespace Cango::TaskDesign {
/// @brief Concept for objects that can be formatted using fmt.
template <typename TObject>
concept IsFormattableObject = requires(const TObject & obj, fmt::memory_buffer & buffer) {
{ obj.Format(buffer) } -> std::convertible_to<void>;
};
/// @brief Base class for formattable objects, providing a Format method that formats the object to a fmt buffer.
struct FormattableObject {
/// @brief Formats the object into the provided fmt buffer.
/// @param buffer The fmt memory buffer for formatting.
/// @note Override this method to customize the formatting of derived objects.
virtual void Format(fmt::memory_buffer& buffer) const noexcept = 0;
};
// 补充备选方案FormattableVirant
template<typename... Ts>
struct FormattableVariant : std::variant<Ts...> {
using std::variant<Ts...>::variant;
void Format(fmt::memory_buffer& buffer) const noexcept {
std::visit([&](const auto& val) {
// 使用 if constexpr 分支处理不同的类型
fmt::format_to(std::back_inserter(buffer), "{}", val);
}, *this);
}
};
/// @brief Formats a collection of formattable objects as a list.
/// @param buffer The fmt memory buffer for formatting.
/// @param objects A span of formattable objects to be formatted.
template <IsFormattableObject TObject>
void Format(fmt::memory_buffer& buffer, const std::span<const TObject> objects) noexcept {
const auto size = objects.size();
if (size == 0) {
fmt::format_to(buffer, "[]");
return;
}
fmt::format_to(buffer, "[");
for (size_t i = 0; i < size; ++i) {
objects[i].Format(buffer);
if (i < size - 1) {
fmt::format_to(buffer, ", ");
}
}
fmt::format_to(buffer, "]");
}
/// @brief Outputs a collection of formattable objects as a formatted list to a stream.
template <IsFormattableObject TObject>
std::ostream& operator<<(std::ostream& stream, const std::span<const TObject> objects) noexcept {
fmt::memory_buffer buffer;
Format(buffer, objects);
return stream << fmt::to_string(buffer);
}
//补充一个 FormattableVarriant
}
#include <FormatObject.h>
#include <vector>
// Example of a struct inheriting from FormattableObject
struct MyStruct : public Cango::TaskDesign::FormattableObject {
Cango::TaskDesign::FormattableVariant<int, double, std::string> data;
void Format(fmt::memory_buffer& buffer) const noexcept override {
data.Format(buffer);
}
};
int main() {
// Test single object formatting
MyStruct s;
s.data = 42;
fmt::print("Single object: {}\n", s);
return 0;
}
可能会导致未定义的情况,还是不用为妙
旧的 FormattableObject.hpp 本身并不支持含有选项的格式化,所作所为只是利用 std::ostream& 简单格式化而已。 希望有更好的实现方案。