第11.2章: 为 I/O 定义自定义类型

English

概述

fast_io 用定制点让你定义自己的类型如何被打印和扫描。主要有四个定制点,各有不同的取舍。

1. 定制点一览

定制点 最适合 速度 安全性
print_reserve_define 大小固定的类型(整数、浮点数、枚举、小结构体) 最快 如果算错大小就不安全
print_scatter_define 已经以连续字符串形式存放的类型(字符串字面量、枚举名) 无复制 安全
print_define 复杂/复合类型(最灵活) 中等 安全
scan_context_define 表示形式会跨越多次缓冲区填充的类型(流式解析) 不适用(输入) 安全

2. print_reserve_define — 最快(但危险)

print_reserve_define 直接写进 I/O 缓冲区。它是最快的方法 — 甚至比返回数组还快,因为它复用已有的 I/O 缓冲区内存,没有任何额外的分配或复制。

危险:你必须正确计算缓冲区大小。如果 print_reserve_size 返回的值太小,你就会写越界 — 这是未定义行为,可能破坏内存。

#include <fast_io.h>

struct Point {
    int x;
    int y;
};

// Step 1: Tell fast_io the maximum buffer size needed
template <::std::integral char_type>
inline constexpr ::std::size_t print_reserve_size(
    ::fast_io::io_reserve_type_t<char_type, Point>) noexcept
{
    // Two ints, a comma, parentheses, and a space
    // Max int: 10 digits + sign = 11 chars each
    // "(x, y)" = 1 + 11 + 2 + 11 + 1 = 26
    return 26;
}

// Step 2: Write into the buffer, return pointer past last written char
template <::std::integral char_type>
inline constexpr char_type* print_reserve_define(
    ::fast_io::io_reserve_type_t<char_type, Point>,
    char_type* iter, Point p) noexcept
{
    // Use character literals appropriate for each character type
    // This handles EBCDIC and other non-ASCII encodings correctly
    if constexpr (::std::same_as<char_type, char>) {
        *iter++ = '(';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.x);
        *iter++ = ',';
        *iter++ = ' ';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.y);
        *iter++ = ')';
    } else if constexpr (::std::same_as<char_type, char8_t>) {
        *iter++ = u8'(';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.x);
        *iter++ = u8',';
        *iter++ = u8' ';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.y);
        *iter++ = u8')';
    } else if constexpr (::std::same_as<char_type, char16_t>) {
        *iter++ = u'(';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.x);
        *iter++ = u',';
        *iter++ = u' ';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.y);
        *iter++ = u')';
    } else if constexpr (::std::same_as<char_type, char32_t>) {
        *iter++ = U'(';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.x);
        *iter++ = U',';
        *iter++ = U' ';
        iter = ::fast_io::detail::print_reserve_define_integer<10>(iter, p.y);
        *iter++ = U')';
    }
    return iter;
}

int main() {
    using namespace ::fast_io::iomnp;
    Point p{42, 100};
    println("Point: ", p); // Output: Point: (42, 100)
}

何时使用:当你的类型有一个固定的小规模文本表示,且你能在编译期精确算出大小时。

3. print_scatter_define — 无复制

print_scatter_define 返回指向已有数据的指针和长度。不需要复制 — I/O 系统直接引用你的数据。


#include <fast_io.h>

enum class Color { Red, Green, Blue };

template <::std::integral char_type>
inline constexpr ::fast_io::basic_io_scatter_t<char_type>
print_scatter_define(::fast_io::io_reserve_type_t<char_type, Color>, Color c) noexcept
{
    // Use character type-specific string literals
    if constexpr (::std::same_as<char_type, char>) {
        switch (c) {
            case Color::Red:   return {"Red", 3};
            case Color::Green: return {"Green", 5};
            case Color::Blue:  return {"Blue", 4};
        }
        return {"Unknown", 7};
    } else if constexpr (::std::same_as<char_type, char8_t>) {
        switch (c) {
            case Color::Red:   return {u8"Red", 3};
            case Color::Green: return {u8"Green", 5};
            case Color::Blue:  return {u8"Blue", 4};
        }
        return {u8"Unknown", 7};
    } else if constexpr (::std::same_as<char_type, char16_t>) {
        switch (c) {
            case Color::Red:   return {u"Red", 3};
            case Color::Green: return {u"Green", 5};
            case Color::Blue:  return {u"Blue", 4};
        }
        return {u"Unknown", 7};
    } else if constexpr (::std::same_as<char_type, char32_t>) {
        switch (c) {
            case Color::Red:   return {U"Red", 3};
            case Color::Green: return {U"Green", 5};
            case Color::Blue:  return {U"Blue", 4};
        }
        return {U"Unknown", 7};
    }
}

int main() {
    using namespace ::fast_io::iomnp;
    println("Color: ", Color::Green); // Output: Color: Green
}

何时使用:当你的类型的文本表示在内存中已经是一段连续字符串时(字符串字面量、枚举名等)。

4. print_define — 最灵活

print_define 用 print_freestanding 写到输出流上。它最灵活,但也是三种打印方法里最慢的(依然很快 — 只是不如 print_reserve_define 那么快)。


#include <fast_io.h>

struct Person {
    ::fast_io::string name;
    int age;
};

template <::std::integral char_type, typename T>
inline constexpr void print_define(
    ::fast_io::io_reserve_type_t<char_type, Person>,
    T& out, Person const& p)
{
    print_freestanding(out, p.name, " (age: ", p.age, ")");
}

int main() {
    using namespace ::fast_io::iomnp;
    Person alice{::fast_io::string{"Alice"}, 30};
    println("Person: ", alice); // Output: Person: Alice (age: 30)
}

何时使用:用于输出依赖运行时数据的复杂类型,或者当你需要组合多个子对象时。

5. scan_context_define — 多次调用的扫描

scan_context_define 用于文本表示可能跨越多次缓冲区填充的类型。它用一个上下文在多次调用之间记录解析状态。

你必须提供三个函数:

  1. scan_context_type — 声明上下文类型
  2. scan_context_define — 从缓冲区解析,返回 partial/ok/invalid
  3. scan_context_eof_define — 处理输入结束

#include <fast_io.h>

struct MyType {
    int value;
};

// Context type holds partial parse state
struct MyContext {
    int accumulated{};
    bool started{};
};

// Step 1: Declare context type
template <::std::integral char_type>
inline constexpr auto scan_context_type(
    ::fast_io::io_reserve_type_t<char_type, MyType>) noexcept
{
    return ::fast_io::io_type_t<MyContext>{};
}

// Step 2: Parse from buffer
template <::std::integral char_type>
inline constexpr ::fast_io::parse_result<char_type const*>
scan_context_define(
    ::fast_io::io_reserve_type_t<char_type, MyType>,
    MyContext& ctx,
    char_type const* first, char_type const* last,
    MyType& t) noexcept
{
    // Parse digits, accumulating into ctx.accumulated
    for (auto it = first; it != last; ++it) {
        char_type ch = *it;
        if (ch >= static_cast<char_type>('0') &&
            ch <= static_cast<char_type>('9')) {
            ctx.accumulated = ctx.accumulated * 10 +
                (static_cast<int>(ch) - static_cast<int>('0'));
            ctx.started = true;
        } else {
            t.value = ctx.accumulated;
            return {it, ::fast_io::parse_code::ok};
        }
    }
    return {last, ::fast_io::parse_code::partial}; // need more data
}

// Step 3: Handle EOF
template <::std::integral char_type>
inline constexpr ::fast_io::parse_code
scan_context_eof_define(
    ::fast_io::io_reserve_type_t<char_type, MyType>,
    MyContext& ctx, MyType& t) noexcept
{
    if (ctx.started) {
        t.value = ctx.accumulated;
        return ::fast_io::parse_code::ok;
    }
    return ::fast_io::parse_code::invalid;
}

6. 我该用哪一个?

你的类型是…… 那么用……
大小固定、编译期可计算(整数、小结构体) print_reserve_define(最快)
已经是连续字符串(枚举名、字面量) print_scatter_define(无复制)
复杂,由多个部分组成 print_define(最灵活)
可能跨越多次缓冲区读取(流式解析) scan_context_define

要点