c++ 是否有类型安全的编译时检查printf的实现?

jv4diomz  于 2022-11-19  发布在  其他
关注(0)|答案(3)|浏览(232)

我看到过许多类型安全printf实现,但其中大多数都使用异常来引发类型错误。
作为练习,我使用字符串文字实现了类似printf格式的原型,它似乎解决了旧的、好的printf家族的所有问题(除了使用从外部源读取的格式,这总是不安全)。

范例:

int main(int argc, char* argv[])
{
    std::cout << "const char*: %s, std::string: %s\n"_format("ONE", std::string{"TWO"});
    std::cout << "user defined: %*\n"_format(std::complex<int>{1, 2});
    std::cout << "hex: 0x%x, int: %d\n"_format(16, 123);
    std::cout << "double.2: %.2f, double: %f\n"_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)";
}

完整、脏、不完整或未优化的代码为here
生成的.s显示在运行时不进行任何文本处理,即使变量不是const,而是从argv中获取的示例。传递错误的变量类型或使用参数计数会导致严重的编译错误,可以使用静态Assert或概念来改善这种错误。
这只是演习,问题是:是否有任何库支持这样的构造?为什么这样的方法不是c++标准的一部分?

bakd9h0s

bakd9h0s1#

{fmt} library和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语法。

aij0ehis

aij0ehis2#

有这样的图书馆吗?GCC(和其他编译器)理解printf字符串的语法,如果类型不匹配,可以被说服发出编译时错误。
为什么这种方法不是C++的一部分?因为Bjarne最初提出了iostream,它也可以实现类型安全IO,没有人对此有足够的强烈感受,从而提出了替代建议。

zengzsys

zengzsys3#

使用 variadic templates,您可以简单地拥有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.

相关问题