Skip to content

Latest commit

 

History

History
120 lines (80 loc) · 2.99 KB

README.asc

File metadata and controls

120 lines (80 loc) · 2.99 KB

tsprintf

What is tsprintf?

tsprintf stands for type-safe printf.

Remind me what’s printf and its problems?

printf is a function used to print formatted string with parameters into the console. It comes with C standard library and has its drawbacks:

  • type information loss leads to inconsistency of types in the format string and the arguments,

  • there is no way to add a custom type, and

  • vulnerable to attacks.

Due to these reasons C++ offers iostreams:

  • no loss of type information,

  • easy to extend overloading the stream operator, and

  • doesn’t play nasty games with the stack.

Though, printf has very nice syntax in contrast of awkward and verbose iomanip thingy things.

What does tsprintf offer to me?

This is a proof-of-concept that type-safe printf can be implemented. It works with GCC 8; other compilers might not be supported, and I welcome you to contribute.

tsprintf gives you:

  • a drop-in replacement (with limited support) of printf, and

  • a user defined literal to call printf.

Drop-in replacement

Simply call:

using namespace tsprintf;
tsprint("integer is %d\n"_lit, 42);

If it gets printed you are safe. Note, _lit is required.

Check out these examples of potential problems checked in compile-time:

  • few and extra arguments,

tsprint("integer is %d\n"_lit, 42, 42);  // extra arguments
tsprint("integer is %d\n"_lit);          // too few arguments
  • type mismatch,

tsprint("integer is %d\n"_lit, 42.0);
  • and unsecure use of dynamic format strings. The specialization of tsprintf taking a char const * is marked as depricated so the compiler will warn you:

// warning: 'constexpr void tsprintf::tsprint(Fmt&&, Args&& ...)
// [with Fmt = const char*&; Args = {int&}]' is deprecated:
// can't validate a dynamic format string [-Wdeprecated-declarations]

char const *fmt = "integer is %d\n";
tsprint(fmt, 42);

User defined literal

With the UDL you can always be sure that your call passes static checks. Look at examples:

"integer is %d\n"_printf(42);
"integer is %d\n"_printf(42, 42);  // extra arguments
"integer is %d\n"_printf();        // too few arguments
"integer is %d\n"_printf(42.0);    // type mismatch

Requirements

  • GCC 8 or above; other compilers not tested

Note
GCC 8 shows deprecation warnings twice.

Compilation && launching

On macOS run:

$ brew install gcc
$ git clone https://github.com/deni64k/tsprintf tsprintf && cd $_
$ make CXX=g++-8

It compiles and runs the example in main.cxx.

Tip
Take a look at the compiler output and play with the example.

Food for thoughts

It shows that type-safety can be implemented around formatting string literals. It can find an application in:

  • manipulating/rewriting the format string to allow Python’s {}-analog or fix problems casting arguments to proper types,

  • adaptors for query languages such as SQL,

  • you name it.

License