Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ functional programming code snippets

I have been working on a project called: Functional Programming Features of C++11/14 (for one of my subjects at university). There are several existing sources and similar presentations about such topics and I found one not so long ago which contained several code snippets that I haven't managed to understand completely (and somehow they can be connected to functional programming). Snippets A and B belonged to recursion and C belonged to lazy evaluation. I'd like to share them with you below:

Snippet A:

#include <iostream>

template <int N>
struct Factorial {
    static int const val = N * Factorial<N - 1>::val;
};

template <>
struct Factorial <0> {
    static int const val = 1;
};

int main() {
    int factorial_of_6 = Factorial<6>::val;
    std::cout << factorial_of_6 << std::endl;
    return 0;
}

Was the point here compile time evaluation (in order to avoid runtime computations and improve performance)? Or are there other advantages, too?

Snippet B:

#include <iostream>

template <int ...>
struct my_sum;

template <>
struct my_sum <> {
    static const int value {0};
};

template <int i, int ... tail>
struct my_sum <i, tail ...> {
    static const int value = i + my_sum<tail ...>::value;
};

int main() {
    int sum {my_sum<1, 2, 3, 4, 5>::value};
    std::cout << sum << std::endl;
    return 0;
}

Same question applies as above.

And here is another snippet which is maybe similar:

Snippet C:

#include <iostream>

template <typename... Args>
void some_function (Args ...) {
    std::cout << sizeof...(Args) << std::endl;
}

int main() {
    some_function ("Every little thing gonna be alright...", 1.0 / 0.0);
    return 0;
}

"The presentation said: C++ is eager but the following will work." Is its point that until I don't care about the given expressions I can tell the quantity of them?

Please, be as specific and detailed as possible, and thank you very much for your patience and help in advance. :)

like image 759
mindthegap Avatar asked Mar 24 '16 16:03

mindthegap


1 Answers

Snippet A

This is called Template Metaprogramming, which is basically a technique using templates to generate code at compile time. This increases run time performance, because the calculations aren't done at run time, but rather at compile time.

Snippet A calculates the factorial of a given number at compile time:

template <int N>
struct Factorial {
    static int const val = N * Factorial<N - 1>::val;
};

This defines a struct Factorial as a template which takes an int. In that struct, there is a static const variable. The variable is static, so that you don't have to create an instance of Factorial to access it, you can just use Factorial::val instead of

Factorial factorial;
factorial.val;

The variable is const because the factorial of a given number is always the same, and because the project won't compile if it isn't const, because the compiler has no way of knowing if you change the variable someplace else.

The variable has a value of N * Factorial<N - 1::val;, which basically multiplies N with the factorial of the previous number. This is because of how factorials are defined (3! = 2! * 3 = 1! * 2 * 3 = 1 * 2 * 3 = 6).

template <>
struct Factorial <0> {
    static int const val = 1;
};

This defines a fully specialized struct for N = 0. This is really important, or else the recursion used in the previous function will never stop.

Then, getting the factorial of a number N is easy, Factorial<N>::val. This will be calculated at compile time.


Snippet B

This is also Template Metaprogramming.

template <int ...>
struct my_sum;

This defines an empty template struct which takes an int... (a Parameter Pack), so that it can be specialized (see next point).

template <>
struct my_sum <> {
    static const int value {0};
};

This specializes the struct my_sum, when no template arguments are given (this is because of the Parameter Pack, which can be empty, and so the template arguments will be empty when the Parameter Pack is expanded). The value is static and const because of the same reasons as before, and it is 0 initialized using an initializer list (for an int, there is no difference between int i = 0; and int i{ 0 };).

template <int i, int ... tail>
struct my_sum <i, tail ...> {
    static const int value = i + my_sum<tail ...>::value;
};

This defines the struct my_sum as a template which takes 2 template arguments, an int and a int parameter pack. This is used to get the value of the first value of a parameter pack, because you can't index a parameter pack (it's not an array). Then, value is initialized as i (the first value of the pack) plus the value of the other values as parameter pack, which is expanded (using the ...):

int sum = my_sum<1, 2, 3>::value;

This calls my_sum<int i, int... tail>, i is 1 and tail is 2, 3. value is i + my_sum<tail...>::value, so it is 1 + my_sum<2, 3>. my_sum<2, 3> calls the same function again, 2 + my_sum<3>::value. Now we have 1 + 2 + my_sum<3>::value. my_sum<3>::value calls again the same function, but now the parameter pack is empty! So value is 1 + 2 + 3 + my_sum<>::value. my_sum<>::value is 0 (as defined) and so value = 1 + 2 + 3 + 0.


Snippet C

The expression is evaluated, but the program doesn't crash because the expression when evaluated, is a double. Only when the expression is an int does it crash with a Integer division by zero exception. If you were to do this:

int zero = 0; 
double d = 1.0 / zero;

Then d would have the value inf.

The function some_function is a template function which takes as template parameter a parameter pack. Then it calls sizeof... which counts the elements in the parameter pack, and outputs it usingstd::cout.

like image 188
Rakete1111 Avatar answered Oct 23 '22 05:10

Rakete1111