Folding similar templates into one

Meta jared771 at gmail.com
Wed Jun 26 19:03:34 PDT 2013


I apologize for the title, as I couldn't come up with something 
succinct and descriptive enough while still being reasonably 
short. We all know how powerful templates are in C++/D, but they 
have that damning cost of needing to generate new code for each 
new template argument. For example:

string format(string fmtStr)(T data)
{
     ...
}

As fmtStr is a compile-time template argument, the compiler will 
have to generate N nearly-identical format functions for N 
strings that format is instantiated with. This "template bloat" 
increases exponentially as more template arguments are added. 
Take std.exception.assertNotThrown:

void assertNotThrown(T : Throwable = Exception, E)
                     (lazy E expression,
                      string msg = null,

                      //Could be compile-time, but aren't,
                      //because of template bloat worries
                      string file = __FILE__,
                      size_t line = __LINE__)
{
     try
         expression();
     catch(T t)
     {
         immutable message = msg.empty ? t.msg : msg;
         immutable tail = message.empty ? "." : ": " ~ message;
         throw new AssertError(format("assertNotThrown failed: %s 
was thrown%s",
                                      T.stringof,
                                      tail),
                               file,
                               line,
                               t);
     }
}

The arguments file and line are known at compile time, but should 
not be made template arguments because there is a potential to 
create N*M copies of assertNotThrown, given N values of __FILE__ 
and M values of __LINE__.

Adding large numbers of template arguments spuriously can cause 
an explosion in binary size, especially when templates are very 
heavily used, such as in most D code. Furthermore, programmers 
worried about such bloat will undoubtedly take the safe route, 
and try to move as many arguments as possible from the 
compile-time parameter list to run-time. This means that 
generated code will not necessarily be efficient as it should be.

I'd like to ask, however, why should this be? The arguments line 
and file will only ever be strings and ints. They don't vary in 
type, only value, and changing those values will not affect how 
the function works. You could easily substitute out "exception.d" 
with "stdio.d", and the functionality will be unchanged.

This remains true for values of the same type. It doesn't matter 
whether you pass 1000, 10_000, or 100_000 as a template argument. 
The way the function operates is not observably different (note 
that for this to be true, we have to assume that static if does 
not exist). This is in contrast to passing a completely different 
type for T (say, RangeError), which may change how the function 
works.

I'd like to know, are there any template "optimizations" that DMD 
currently implements which mitigates such a situation such as 
with assertNotThrown, where a compile-time argument may vary only 
in value, and not type? Judging from the fact that this function 
was changed so that that __FILE__ and __LINE__ are taking as 
run-time parameters, I guess the answer is no. Is there anything 
we can do about this?


More information about the Digitalmars-d mailing list