Friday, December 14, 2012

renderPaz: Speed up your Android NDK build

My boss co-worker posted an article on Android NDK build configuration:
renderPaz: Speed up your Android NDK build: Because of a bug in the recent NDK-r8c release which effectively causes all builds to be clean builds, my frustrations with our Android build time reached a tipping point…

Thursday, December 13, 2012

Building on Printf: A Boost.Format Alternative

This post is coming soon!

C++11 and Constexpr: A Fully Typesafe Printf

This post is still in progress!


/* Filename: tsprintf.cpp / tsprintf.hpp
   Author: Adam Berger (adam@addaon.com)
   Make: clang++ -std=c++11 -Wall -Wextra -Wformat=2 -DTSP_CPP tsprintf.cpp
   Date: 22 June 2012 */

/*******************************************************************************

WHY THIS DOCUMENT EXISTS:

The purpose of this file is both to demonstrate the effective use of constexprs,
a new feature of C++11, and to provide a production-quality typesafe wrapper for
the printf(3) family of functions. During the development of Zombie Highway,
shortcomings in in printf(3), std::ostream, and Boost.Format motivated this
alternate approach. Requirements are full type safety (not only must crashes due
to type mismatch be avoided, but it must be impossible to have a partially or
unexpectedly formatted string), a wrapping formatter class that allows caching
of formatted strings (explored in a later post), and performance nearly the same
as that of printf(3). Specifically not required is support for printing of user-
defined types.

There are nearly as many variants of printf(3) as there are of Unix. We shall
focus on the POSIX.1-2008 variant, described in IEEE 1003.1-2008 [Spec]:
We will unconditionally support the extensions marked [CX], including positional
arguments, critical for localization. [XSI] extensions will be conditionally
supported unless TSP_SUPPORT_XSI is defined as a false value. This variant
includes the (mis-)feature of %n, which requires as an argument a pointer "into
which is written the number of bytes written to the output so far." We will
support this feature unless TSP_SUPPORT_FSN is defined as a false value.


THE PROJECT

We would like to be able to format the format string "%i %s", for example, with
the arguments (3, "hello"), but not with the arguments (3.0, 'h'). To accomplish
this, we require the type of a format string to depend on the format specifiers
within it. For our example, we would like to easily construct an object of type
FormatString<int, char const *> from our literal format string "%i %s". That
object, in turn, will provide methods that map directly to the printf(3) family
of functions.

That is, we wish to be able to write:
    $F("%i %s").printf(3, "hello")
or, if TSP_SUPPORT_APF:
    char * str = $F("%i %s").aprintf(3, "hello")
or even, if TSP_SUPPORT_WPF:
    $F(L"%i %s").printf(3, "hello")
but certainly not:
    $F("%i %s).printf(3)

An alternative approach would be to provide free functions printf(), etc, that
take a typesafe format string argument, as printf($F("%i %s", 3, "hello"); but
this is prone to errors where the $F is omitted, so we choose the method-based
approach instead.

Because raw user-defined literals are not available for strings in C++11, we are
forced to use a somewhat complex approach to achieve this behavior. As indicated
in the examples, we will define a macro $F() that takes as its argument a string
literal (and only a string literal), and evaluates to a constexpr value of the
appropriate type. (The choice of $F is for brevity; however, use of the $ symbol
in identifiers is a gcc/clang extension and is not standard C++. If unavailable
for you, define TSP_SUPPORT_DOL as a false value and use FORMAT_STRING instead.)

How do we define a macro that takes a string literal and returns a value whose
type depends on the contents of the string literal? We'll need to use all three
of C++11s metaprogramming languages. Constexpr functions, introduced in C++11,
provide fast and (relatively) readable calculations with compile-time functions.
We'll use a constexpr function to parse a specifier from the format string - but
being a function that returns a value of a fixed type, we'll have to represent
the result of this parsing as an integer, enum, or similar type. Templates, long
a part of C++, allow the conversion of finite domains of values into types, and
this is exactly what we need for handling the result of our constexpr function.
Finally, macros, inherited from C, are great at textual substition, allowing
bending of syntax rules a bit. We'll use them to allow a string literal to
appear multiple times in an expresion while still remaining a literal, which
will allow us to call our parse function multiple times. One limitation we will
run into here is the inability to have loops in macro metaprogramming. We will
thus arbitrarily limit the maximum number of format specifiers in a single
format string to 31; this can easily be adjusted as needed.

The format string $F("%%") represents the string "%"; that is, it is a compile-
time-constant format string that takes no arguments. For many such strings, we
can provide valuable optimizations - $F("hello").sprintf(buf) can call memcpy(3)
rather than sprintf(3), but $F("%%").sprintf(buf) does not easily allow this.
An additional macro, $C (or CONST_STRING if TSP_SUPPORT_DOL is false), allows
direct definition of such compile-time-constant strings without parsing the
string literal as a format string, so that $F("%%") and $C("%") represent the
same value, but the latter allows a wider range of optimizations.

Let's get coding:

*******************************************************************************/

#ifndef TSPRINTF_H
#define TSPRINTF_H

/* Set the default values of some #defines to describe what dialect and features
   of printf(3) and family we support. These symbols can be defined before this
   file is included, or on the command line, to override them. */

#ifndef TSP_SUPPORT_XSI   /* support extensions marked [XSI] in Spec */
#define TSP_SUPPORT_XSI 1 /* on by default */
#endif

#ifndef TSP_SUPPORT_FSN   /* support the %n format specifier from Spec */
#define TSP_SUPPORT_FSN 1 /* on by default */
#endif

#ifndef TSP_SUPPORT_WPF   /* support the wprintf(3) family of functions */
#define TSP_SUPPORT_WPF 1 /* on by default */
#endif

#ifndef TSP_SUPPORT_APF   /* support the GNU C asprintf(3) extension */
#define TSP_SUPPORT_APF 1
#endif

#ifndef TSP_SUPPORT_TSF   /* provide hooks for tsformat, a future project */
#define TSP_SUPPORT_TSF 1 /* on by default */
#endif

#ifndef TSP_SUPPORT_DOL   /* define $F as a shortcut for FORMAT_STRING */
#define TSP_SUPPORT_DOL 1 /* on by default */
#endif

/* TODO! */

/* Provide the user-facing macros that allow definition of format strings. */

#if TSP_SUPPORT_DOL
#define $F(literal) FORMAT_STRING(literal)
#define $C(literal) CONST_STRING(literal)
#endif /* TSP_SUPPORT_DOL*/

#endif /*TSPRINTF_H*/

/************************ TEST CODE *******************************************/

#ifdef TSP_CPP /* Define this to include testing code if build as .cpp */

int main() {
    /* TODO! */
    return 0;
}

#endif /*TSP_CPP*/