0

Given the following code:

#include <iostream>

template <int X, int Y>
int foo(int v) // dummy parameter
{
    return v * X + v / Y; // dummy calculation
}

int main()
{
    // x, y, v are only known at runtime
    int x = 4;
    int y = 6;
    int v = 3;

    int result = 0;

    if (x == 1 && y == 1) result = foo<1, 1>(v);
    if (x == 1 && y == 3) result = foo<1, 3>(v);
    if (x == 5 && y == 1) result = foo<5, 1>(v);
    if (x == 4 && y == 6) result = foo<4, 6>(v);
    if (x == 8 && y == 4) result = foo<8, 4>(v);
    // ...

    std::cout << result << std::endl;
}

I would like to instantiate foo for different combinations of X and Y as shown in the if cascade in main.

This however can become quite ugly (long). Is there a possibility using C++14 (e.g. by using the preprocessor) to generate this code given a list of needed combination?

Tobias Hermann
  • 9,936
  • 6
  • 61
  • 134

3 Answers3

3

This is a version using recursion.

#include <iostream>
#include <utility>
#include <stdexcept>

template <int X, int Y>
int foo(int v) // dummy parameter
{
    return v * X + v / Y; // dummy calculation
}

template <std::size_t index = 0>
int foo(int x, int y, int v) {
    constexpr std::pair<int, int> numbers[] = {{1, 1}, {1, 3}, {5, 1}, {4, 6}, {8, 4}};
    if constexpr (index < sizeof numbers / sizeof *numbers) {
        if (numbers[index].first == x && numbers[index].second == y) {
            return foo<numbers[index].first, numbers[index].second>(v);
        }
        return foo<index + 1>(x, y, v);
    } else { //no match
        throw std::runtime_error("No matching pair found");
    }
}

int main() {
    // x, y, v are only known at runtime
    int x = 4;
    int y = 6;
    int v = 3;

    int result = foo(x, y, v);

    std::cout << result << std::endl;
}

If you don't have C++17 you can replace if constexpr with tag dispatching. Both clang and gcc manage to optimize out the code starting at -O1.

nwp
  • 9,623
  • 5
  • 38
  • 68
  • Nice, I think from C++14 on you should be able to make `foo` constexpr as the `throw` should only be used on error and then you got an error. – Surt Nov 25 '17 at 12:11
1

Generating the code and instances for known pairs of x and y, is easy if that is just what you want.

#define XYLIST \
X(1,1)\
X(1,3)\
X(5,1)\
X(4,6)\
X(8,4)
// etc.

#define X(X1, Y1) \
if (X1 == 1 && Y1 == 1) result = foo<X1, Y1>(v); else

XYLIST
{ throw "failed"; }

#undef X

Added an "else" as there should be not be two lines that are the same. And a "throw" at the end to finish the else-cascade.

Surt
  • 15,501
  • 3
  • 23
  • 39
  • Thanks! I [used](https://github.com/Dobiasd/frugally-deep/commit/0dd09ba17be4ee3648639f2a13d1913d1e2d5d1d) your solution. :) – Tobias Hermann Nov 25 '17 at 10:51
0

You can convert them into constant expressions. If incase values are known before hand

#include <iostream>

template <int X, int Y>
int foo(int v) // dummy parameter
{
    return v * X + v / Y; // dummy calculation
}

int main()
{
    // x, y, v are only known at runtime
    const int x = 4;
    const int y = 6;
    int v = 3;

   int result = 0;

   result = foo<x, y>(v);


    std::cout << result << std::endl;
}

However, Templates are not algorithm to be executed, but macros to be expanded to produce code. I would not recommend using a template for the same data type , in this case, you're only using int. Hence, a template is not particularly required as per coding standards.

Pass these values in a function itself.

Method 1 : If all you have are integers.

int foo(int v, int X, int Y) // dummy parameter
{
    return v * X + v / Y; // dummy calculation
}
int main()
{
    // x, y, v are only known at runtime
    int x = 4;
    int y = 6;
    int v = 3;

    int result = 0;

    result = foo(v,x,y);
}

Method 2: If anything can be passed, i.e, double data-type ,etc.

template <typename T>
T foo(int v, T Y, T Y) // dummy parameter
{
    return v * X + v / Y; // dummy calculation
}
Shivansh Jagga
  • 1,541
  • 1
  • 15
  • 24
  • Thanks, but I know that in the case above one would simply pass the integer parameters per value at runtime. ;-) This is just a minimal example, a reduced version of a problem where knowing some of the parameters at compile time is beneficial for the performance (sizes of libEigen matrices in and im2col implementation of a conv layer in a deep learning library). – Tobias Hermann Nov 25 '17 at 09:32
  • Why would you not recommend using a template for the same data type? Non-type templates are useful. – nwp Nov 25 '17 at 10:32