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

Viewed 1519

I saw many implementation of type-safe printf, but most of them are using exception to raise type errors.

As exercise, I implemented prototype of printf-like formatting using string literals, and it seems to solve all the problems with old, good printf family (except using format read from external source which is always not safe).

Example:

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

Full, dirty and not complete nor optimized code is here

Generated .s shows that no text processing is done in runtime, even if variables are not const, but taken for example from argv. Passing wrong variable type, or using argument count cause ugly compilation error, which could be improved with static asserts or concepts.

This is only exercise, and the question is: Is it any library, which supports such constructions, and why such approach is not part of c++ standard?

3 Answers

The {fmt} library and C++20 std::format have compile-time format string checks. For example (https://godbolt.org/z/vr57bqzb3):

#include <fmt/core.h>

int main() {
  std::string s = fmt::format("{:d}", "I am not a number");
}

gives a compile-time error because d is an invalid format specifier for a string. This uses Python-like format string syntax, not printf syntax though.

Is there any such libray? Sort of: GCC (and other compilers) understand the syntax of printf format strings, and can be persuaded to issue a compile time error if the types don't match.

Why is such an approach not part of C++? Because Bjarne originally came up with iostream which also does typesafe IO, and nobody has felt strongly enough about this to come up with an alternative proposal.

Using variadic templates, you can simply have a custom typesafe version of 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++;
    }

};

Check it:

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", '%');

Output:

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