是否有任何类型安全的、编译时检查的 printf 的实现

Is there any implementation of type-safe, compile-time checked printf?

本文关键字:检查 printf 实现 编译 任何 类型安全 是否      更新时间:2023-10-16

我看到许多类型安全printf的实现,但大多数都使用异常来引发类型错误。

作为练习,我使用字符串文字实现了类似 printf 格式的原型,它似乎解决了旧的、好的 printf 系列的所有问题(除了使用从外部源读取的格式,这总是不安全的(。

例:

int main(int argc, char* argv[])
{
    std::cout << "const char*: %s, std::string: %sn"_format("ONE", std::string{"TWO"});
    std::cout << "user defined: %*n"_format(std::complex<int>{1, 2});
    std::cout << "hex: 0x%x, int: %dn"_format(16, 123);
    std::cout << "double.2: %.2f, double: %fn"_format(13.123123, 12.1);
    std::string s = "p(%d, %d)n"_format(123, 234);
    std::cout << s;
//    not yet working
//    int x, y;
//    std::cin >> "p(%d, %d)"_format(x, y);
//    "p(%d, %d)"_format(x, y) = "p(999, 888)";
}

完整、肮脏、不完整或优化的代码在这里

生成的.s表明在运行时不进行文本处理,即使变量不是const,而是从argv中获取例如。传递错误的变量类型或使用参数计数会导致丑陋的编译错误,这可以通过静态断言或概念来改进。

这只是练习,问题是:是否有任何库支持此类构造,为什么这种方法不是 c++ 标准的一部分?

{fmt} 库和 C++20 std::format具有编译时格式字符串检查。例如 (https://godbolt.org/z/vr57bqzb3(:

#include <fmt/core.h>
int main() {
  std::string s = fmt::format("{:d}", "I am not a number");
}

给出编译时错误,因为d是字符串的无效格式说明符。这使用类似Python的格式字符串语法,而不是printf语法。

有这样的图书馆吗? 排序:GCC(和其他编译器(了解printf格式字符串的语法,如果类型不匹配,可以说服发出编译时错误。

为什么这种方法不是C++的一部分? 因为Bjarne最初提出了iostream,它也做类型安全的IO,没有人对此有足够强烈的感觉来提出替代建议。

使用可变参数模板,您可以简单地拥有 printf 的自定义类型安全版本:

void printf_d(const char* msg) {
    while (*msg) {
        if (*msg == '%') {
            throw std::runtime_error("The parameters are not given properly.");
        }
        std::cout << *msg++;
    }
};
template<typename T, typename... Rest>
void printf_d(const char* msg, T arg, Rest... args) {
    while (*msg) {
        if (*msg == '%') {
            std::cout << arg;
            msg++;
            printf_d(msg, args...);
            return;
        }
        std::cout << *msg++;
    }
};

检查一下:

const char* msg = "% my name is % % and I am % years old, I live in % - The % used to specify the arguments.";
printf_d(msg, "Hello", "NAME", "SURNAME", 100, "Hamburg", '%');

输出:

Hello my name is NAME SURNAME and I am 100 years old, I live in Hamburg - The % used to specify the arguments.