TaciturnJian / Cango.TaskDesign

Design aimed at task solutions
MIT License
1 stars 0 forks source link

更好的格式化方法,支持格式化选项,还可以使用简单的方式拓展 #1

Open TaciturnJian opened 2 months ago

TaciturnJian commented 2 months ago

旧的 FormattableObject.hpp 本身并不支持含有选项的格式化,所作所为只是利用 std::ostream& 简单格式化而已。 希望有更好的实现方案。

Motues commented 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);
}
DylanUnicorn commented 2 months ago

先存着可以打印字符串的 `

include <fmt/format.h>

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; }

`

DylanUnicorn commented 2 months ago

这里又编写了一个可以自由打印单类但是一次性打印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;
}
DylanUnicorn commented 2 months ago

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;
}

可能会导致未定义的情况,还是不用为妙