6

What are the techniques / c++ language facilities-features that provide compile time branching?


A first attempt to enumerate them (I'm expecting additions-corrections) :

  1. Overload resolution : Eg picking the version of the "best" suits the provided arguments

    void F(X& arg);
    void F(X&& arg); 
    
  2. Template specialization : Creating code that runs for "special arguments" - a technique crucial for template metaprogramming and compile time recursion

    template<int N> struct A    { /* implementation */ };
    template<>      struct A<0> { /* specific  code */ };
    
  3. SFINAE & expression sfinae : A special case of (1) that provides the tools for conditional interfaces.

    template<class C, class F>
    auto test(C c, F f) -> decltype((c->*f)(), void()); // 'C' is pointer type
    
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • 1
    How about a simple `if` statement with a compile-time expression? – NPE Jul 31 '14 at 09:56
  • SFINAE is more a compile time boolean expression, not compile time branching. – James Kanze Jul 31 '14 at 09:58
  • 4
    @NPE: I don't think `if` statements can be considered compile-time branching, as both branches of the `if` are required to be compiled, even if the `if` has a constant-expression argument. – Mankarse Jul 31 '14 at 10:00
  • 1
    @Mankarse In general yes, but in optimized build, dead code can be eliminated. – Neil Kirk Jul 31 '14 at 10:01
  • IMHO, Mankarse nailed it for the `if`s. Compile-time branching is something different than dead code elimination. – quetzalcoatl Jul 31 '14 at 10:02
  • @JamesKanze The way it excludes functions from overload resolution (especially in expression SFINAE) provides a methodology that looks like compile time branching. Now ofcourse everything I've posted is up for discussion, and further exploration (that's the goal of the Q) – Nikos Athanasiou Jul 31 '14 at 10:04
  • 1
    The conditional operator can be used at compile time: `int b[17 >= 12 ? 2 : 4];`. – Mankarse Jul 31 '14 at 10:19
  • @Mankarse Would you consider the mere action of instantiation as a branching action? I mean since eg `template struct A : T {};` could have it's layout parametrized and it's behaviour tweaked it seems like a branching choice made at that time. – Nikos Athanasiou Jul 31 '14 at 10:28
  • @NikosAthanasiou: Maybe...? The important question isn't really "what can be considered 'branching'?", but rather "what effects can be achieved by which tools?". [Once you know the properties of something, the classification of that thing no longer matters.](http://lesswrong.com/lw/nm/disguised_queries/) It seems to me that template instantiation is more akin to function application than branching, but certainly the resulting instantiated `class` can have properties that vary depending on `T`. – Mankarse Jul 31 '14 at 10:41
  • SFINAE can equally apply to template specialization. So it is an orthogonal concept to (1) overload resolution and (2) template specialization, and serves to filter out some of the available choices. I wouldn't put in the same list as a third option. – iavr Jul 31 '14 at 14:00
  • Dupe of http://stackoverflow.com/a/23982816/819272 – TemplateRex Aug 01 '14 at 14:26

2 Answers2

2

You can use template Boolean parameters to eliminate run time branching (in release build dead code is eliminated).

template <bool computeMaxNorm = false>
bool CheckConvergence() {

    if (computeMaxNorm) this->residual_max_norm = 0;

    for (size_t i = 0, I = this->X.Count(); i < I; ++i) {
        double abs_res = abs(this->F_X[i]);
        if (abs_res > this->convergenceCriterion) {
            this->isConverged = false;
            if (!computeMaxNorm) return false;
        }
        if (computeMaxNorm) {
            if (abs_res > this->residual_max_norm) this->residual_max_norm = abs_res;
        }
    }
    return this->isConverged = true;
}

problem.CheckConverge<false>() will be faster than problem.CheckConverge<true>(), and this feature will cost no run time branching.

However the CPU branch predictor is usually very good and compile-time branching may make no difference.

ThreeStarProgrammer57
  • 2,906
  • 2
  • 16
  • 24
  • I did some code very similar to this, and examined the output binary. The `if` was removed, and two separate functions were generated. One for `true` and one for `false` – XPlatformer Aug 01 '14 at 08:44
  • Yes I tend to abuse this, with multiple template parameters mixed with standard parameters. This lets you have two sets of default parameters. But this is probably a bad practise ? – ThreeStarProgrammer57 Aug 01 '14 at 09:02
1

Although not strictly compile time branching you could add as fourth option:

4) C++ Macros

  #if SOMETHING
    ...
  #else
    ...
  #endif
101010
  • 41,839
  • 11
  • 94
  • 168
  • 2
    That's preprocessor time - actually text substitution – Nikos Athanasiou Jul 31 '14 at 10:15
  • 2
    Yes that's way I said not strictly compile time, preprocessing though is part of the compile process. – 101010 Jul 31 '14 at 10:16
  • The compiler kicks in [after](http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html) the preprocessor (if we see it the way you say then the liknking is also in the compilation process) – Nikos Athanasiou Jul 31 '14 at 10:17
  • 1
    @NikosAthanasiou In your question you said "compile time" preprocessing is compile time (i.e,. it takes place during the compilation process), linking is also taking place during the compilation process. – 101010 Jul 31 '14 at 10:20
  • OK +1, will grant you the fact that preprocessor metaprogramming is highly used along with template metaprogramming and generative programming, so even though I disagree, I must admit it's a tool to consider – Nikos Athanasiou Jul 31 '14 at 10:24
  • 3
    @NikosAthanasiou: Strictly speaking, all nine phases of translation treated equally by the standard. Even though only *some* of them are "compilation", they are all potentially useful for metaprogramming. – Mankarse Jul 31 '14 at 10:24