Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are variadic templates a potential code bloat?

Variadic templates will enable the rewriting of certain kind of functions into cleaner, type-safe versions. It is the case of printf, as the example given on Wikipedia:

void printf(const char *s)
{
    while (*s) {
        if (*s == '%' && *(++s) != '%')
            throw std::runtime_error("invalid format string: missing arguments");
        std::cout << *s++;
    }
}

template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
    while (*s) {
        if (*s == '%' && *(++s) != '%') {
            std::cout << value;
            ++s;
            printf(s, args...); // call even when *s == 0 to detect extra arguments
            return;
        }
        std::cout << *s++;
    }
    throw std::logic_error("extra arguments provided to printf");
}

But... As far as I understand templates, they imply code duplication for each type combination. So the variadic version of the above printfs would be copied many times. This could be terrible for large functions or classes.

Are variadic template as perilous as standard templates for code duplication? If yes, can the inheritance trick still help?

like image 565
Gabriel Avatar asked Nov 10 '11 09:11

Gabriel


People also ask

Can templates lead to code bloat?

How does template cause the code bloat in C++? Code bloat occurs because compilers generate code for all templated functions in each translation unit that uses them. Back in the day, the duplicate code was not consolidated, which resulted in "code bloat". These days, the duplicate code can be removed at link time.

What is template code bloat?

Code bloat is the production of code that is perceived as unnecessarily long, slow, or otherwise wasteful of resources. It is a problem in Software Development which makes the length of the code of software long unnecessarily.

What is the use of Variadic templates?

With the variadic templates feature, you can define class or function templates that have any number (including zero) of parameters. To achieve this goal, this feature introduces a kind of parameter called parameter pack to represent a list of zero or more parameters for templates.

What are Variadic templates C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.


2 Answers

The short answer is: the "you only pay for what you use" principle still applies exactly as before.

The longer answer can be seen by comparing the generated code for two hypothetical implementations e.g.

#include <iostream>

template <typename T>
void func1(T& v) {
  v = -10;
}

template <typename T1, typename T2>
void func1(T1& v1, T2& v2) {
  func1(v1); func1(v2);
}

// More unused overloads....
template <typename T1, typename T2, typename T3>
void func1(T1& v1, T2& v2, T3& v3) {
  func1(v1); func1(v2); func1(v3);
}

int main() {
  double d;
  int i;
  func1(d);
  func1(i);
  std::cout << "i=" << i << ", d=" << d << std::endl;
  func1(d,i);
  std::cout << "i=" << i << ", d=" << d << std::endl;
}

With a modern compiler this pretty much reduces to exactly what you'd have written if you wanted to avoid templates all together. In this "traditional" C++03 templated code my version of g++ inlines (in the compiler, not keyword sense) the whole lot and there's no obvious hint that the initializations are done via reference in a template function, several times, in different ways.

Compared with the equivalent variadic approach:

#include <iostream>
#include <functional>

void func1() {
  // end recursion
}

template <typename T, typename ...Args>
void func1(T& v, Args&... args) {
  v = -10;
  func1(args...);
}

int main() {
  double d;
  int i;
  func1(d);
  func1(i);
  std::cout << "i=" << i << ", d=" << d << std::endl;
  func1(d,i);
  std::cout << "i=" << i << ", d=" << d << std::endl;
}

This also produces almost identical code - some of the labels and mangled names are different as you'd expect, but the diff of the generated asm produced by g++ -Wall -Wextra -S (a 4.7 snapshot) has no significant differences. The compiler basically is writing all of the overloads your program requires on the fly and then optimizing as before.

The following non template code also produces almost identical output:

#include <iostream>
#include <functional>

int main() {
  double d;
  int i;
  d= -10; i=-10;
  std::cout << "i=" << i << ", d=" << d << std::endl;
  d= -10; i=-10;
  std::cout << "i=" << i << ", d=" << d << std::endl;
}

Here again the only noticeable differences are the labels and symbol names.

The point is a modern compiler can do "what's right" without much hassle in template code. If what you're expressing is simple underneath all the template mechanics the output will be simple. If it's not then the output will be more substantial, but so would the output be if you'd avoided templates entirely.

Where this gets interesting (in my view) however is this: all of my statements were qualified with something like "with an decent modern compiler". If you're writing variadic templates you can almost be certain that what you're using to compile is a decent modern compiler. No clunky old relic compilers support variadic templates.

like image 119
Flexo Avatar answered Oct 27 '22 00:10

Flexo


It could certainly be a problem. One thing that could help is to factor out the common parts:

const char *process(const char *s)
{
  while (*s) {
      if (*s == '%' && *(++s) != '%') {
          ++s;
          return s;
      }
      std::cout << *s++;
  }
  throw std::logic_error("extra arguments provided to printf");
}

template<typename T>
inline const char *process(const char *s,T value)
{
  s = process(s);
  std::cout << value;
  return s;
}

template<typename T, typename... Args>
inline void printf(const char *s, T value, Args... args)
{
  printf(process(s,value),args...);
}
like image 39
Vaughn Cato Avatar answered Oct 27 '22 00:10

Vaughn Cato