leo leo - 3 months ago 8
C++ Question

Not having to list all possibilities with bools and template parameters

(See edit at the end for clarification)

I'm trying to use bool's as template parameters. I'd like to be able to write something like this

#include <iostream>
#include <string>

template<bool a, bool b>
void foo() {
printf("%d %d\n", (int)a, (int)b);
}

int main()
{
int c = 2 ;
c += 2 % 4 - 3 + 1 ;
bool a = c % 2 == 0 ;
bool b = c % 2 == 1 ;
foo<a, b>();
}


but, as expected, I have a


16:9: error: the value of 'a' is not usable in a constant expression
14:10: note: 'bool a' is not const


Of course I could do something like

#include <iostream>
#include <string>

template<bool a, bool b>
void foo() {
printf("%d %d\n", (int)a, (int)b);
}

int main()
{
int c = 2 ;
c += 2 % 4 - 3 + 1 ;
bool a = c % 2 == 0 ;
bool b = c % 2 == 1 ;
if(a) {
if(b) {
foo<true,true>();
} else {
foo<true,false>();
}
} else {
if(b) {
foo<false,true>();
} else {
foo<false,false>();
}
}
}


but it's a bit nasty. Since a and b are bools, is there any way to not have to explicitly list all possibilities ? Could the compiler figure that out by himself ?

EDIT: to clarify: I understand templates are compile time "variables". My question is wether the compiler could automatically generate all the functions, based on the fact that a bool is either true/false (so it's a limite "range" of values, which is what you need to use templates in some sense), and then add the required "plumbing" so that the right function is choosen at runtime.

Thanks,

Answer

Template arguments are evaluated at compile time. Now consider this modification of your example:

#include <iostream>
#include <string>

template<bool a, bool b>
void foo() {
    printf("%d %d\n", (int)a, (int)b);   
}

int main()
{
    int c = 2 ;

    std::cin >> c;        

    bool a = c % 2 == 0 ;
    bool b = c % 2 == 1 ;
    foo<a, b>();
}

Here, there is no way how to evaluate at compile time. Unfortunatelly the compiler cannot distinguish between these two examples.

Compiler cannot generate all the functions itself (if you had ints, would you want it to generate 2**32-1 functions?) unless you ask for specific ones:

#include <iostream>
#include <map>
#include <functional>

template <bool a, bool b>
void func() {
    std::cout << std::boolalpha << a << " " << std::boolalpha << b << std::endl;
}

std::map<bool, std::map<bool, std::function<void ()>>> dispatcher;

template <bool a, bool b>
void register_dispatcher() {
    dispatcher[a][b] = func<a,b>;
}

int main() {
    register_dispatcher<false, false>();
    register_dispatcher<false, true>();
    register_dispatcher<true, false>();
    register_dispatcher<true, true>();

    bool a, b;
    a = false;
    b = true;

    dispatcher[a][b](); 
    // for clarity I don't handle nonexistent keys

    return 0;
}

Note that the code requires C++11.

Now you may want 10 bool arguments and generate each of them. Beware, that's 1024 functions. Not that much, but be careful. Let's do it with 3:

#include <iostream>
#include <map>
#include <functional>

template <bool a, bool b, bool c>
void func() {
    std::cout 
        << std::boolalpha << a << " " 
        << std::boolalpha << b << " "
        << std::boolalpha << c << " "
        << std::endl;
}

std::map<bool, std::map<bool, std::map<bool, std::function<void ()>>>> dispatcher;

template <bool a, bool b, bool c>
void register_dispatcher() {
    dispatcher[a][b][c] = func<a,b, c>;
}

template <bool a, bool b>
void register_dispatcher_2() {
    register_dispatcher<a, b, true>();
    register_dispatcher<a, b, false>();
}

template <bool a>
void register_dispatcher_1() {
    register_dispatcher_2<a, true>();
    register_dispatcher_2<a, false>();
}

void register_dispatcher_0() {
    register_dispatcher_1<true>();
    register_dispatcher_1<false>();
}

int main() {
    register_dispatcher_0();

    bool a, b, c;
    a = false;
    b = true;
    c = true;

    dispatcher[a][b][c]();

    return 0;
}

The code generates one argument at a time. It would start being worth it at some 7 or 8 arguments, or when you start mixing in ints.