PatrykB
PatrykB

Reputation: 1699

Will those if statements be resolved at compile time?

Hey guys I have few questions that I can`t answer by my self.

void someFunc_1(some_known_type_1 bar);
void someFunc_2(some_known_type_2 bar);
void someFunc_3(some_known_type_3 bar);

template < class T, T success> 
void foo(T check) {

    // ...

    auto bar = check;

    if (std::is_same<T, some_known_type_1>::value) {
        someFunc_1((some_known_type_1) bar);
        // ... 
    }
    else if (std::is_same<T, some_known_type_2>::value) {
        someFunc_2((some_known_type_2) bar);
        // ... 
    }
    else if (std::is_same<T, some_known_type_3>::value) {
        someFunc_3((some_known_type_3) bar);
        // ... 
    }

    // ...
}

Code above is a simplest version of my template function to recognize // handle errors. I write this function in that way to avoid code duplication - if I have to use template specialization, I would find that in every version part of the code is repeated.

Questions:
1. Will those if statements be resolved at compile time? I think they will be but I am not sure. Code is compiling i tested it.
2. Normal if I did not use variable auto bar I would end up with compile time errors (type mismatch) for each if statement. Therefore using auto variable is a good idea? Or Is there a better one? (I tried also with function pointers, it worked, but it wasn't performance friendly)





EDIT:
1. Ok so many of you told me that i could overload someFunc, so I done that. but I have new question now: Is it wise to call empty void function?

New Version of the code

///////////////////////////////////
// someFunc:
// - this function in my original code handles different kinds of errors.
// - overloaded for 2 different types (some_known_type_1 & some_known_type_2).
// - with "Catch-all do-nothing" template. 
inline void someFunc (some_known_type_1 bar) {
    // Do something...
}

inline void someFunc (some_known_type_2 bar) {
    // Do something...
}

// Catch-all do-nothing:
template <class T>
inline void someFunc(const T& bar) { /* I am empty... */ };

///////////////////////////////////
// Errchk: (Error check)
template < class T, T success> inline
void errchk(T check) {
    if (check != success) {
        std::cout << "there was an error !!!" << std::endl;

        // Handle Error:
        someFunc( check );
    }
}

// Few additional macros to simplify call to errchk function:
#define ERRCHK_BOOL(_check)   errchk <bool, true> (_check);
#define ERRCHK_TYPE_1(_check) errchk <some_known_type_1, type_1_success_value> (_check);
#define ERRCHK_TYPE_2(_check) errchk <some_known_type_2, type_2_success_value> (_check);

///////////////////////////////////
// program main function:
int main() {
    bool error = false; // this value will be recognized as an error by errchk function;
    ERRCHK_BOOL(error);

    some_known_type_1 error_1 = type_1_error_value // same...
    ERRCHK_TYPE_1(error_1);

    some_known_type_2 error_2 = type_2_error_value // same...
    ERRCHK_TYPE_2(error_2);
}

Ok so if you look closely you will see that I did not overload someFunc for bool type, therefore each time I use ERRCHK_BOOL(***); I will be calling un-specialized version of someFunc template, and this is basically a call to an empty function. Is it a good idea to call empty function?

Upvotes: 1

Views: 1268

Answers (4)

keith
keith

Reputation: 5342

Yes, because T and some_known_type_1 are known at compile time, modern compilers will optimise the if statements away. The reason: they have been designed over the decades to detect dead code branches and eliminate them. If you don't enable any compiler optimisations, then the answer will be no for the same reasons.

Upvotes: 1

Ap31
Ap31

Reputation: 3324

In C++17 you can use constexpr if to make sure;

If you don't have access to C++17, there are still some SFINAE tricks (e.g. you can overload your function on some dummy extra argument) that will do the job, but the chances are your if statements will be resolved at compile time anyway.

As for auto bar part, the question is a bit unclear. Using auto here seems completely normal, although it's exactly the same as T bar.

I have to say though that a simple template specialization could do the same job somewhat clearer perhaps:

void someFunc_1(some_known_type_1 bar);
void someFunc_2(some_known_type_2 bar);
void someFunc_3(some_known_type_3 bar);

template < class T, T success> 
    void foo(T check) {
        // ...
    }

template <some_known_type_1 success> 
    void foo<some_known_type_1, success>(some_known_type_1 check) {
         someFunc_1(check);
         // ... 
    }

Not clear from your code - success parameter is somewhat in the way - but perhaps even function overload will suffice (as others noted).

Upvotes: 4

Marco A.
Marco A.

Reputation: 43662

auto doesn't quite make sense in that context since you're dealing with generic code, use T bar = check; instead.

Your unused branches will likely be optimized (at least clang/gcc do it - check it online https://gcc.godbolt.org/), as others have noted constexpr if in the upcoming C++17 will be your first choice for such a compile-time task.

Final notice: by not having a full insight into your code I can't really say but from just that snippet it seems you're reinventing type overloading or template specialization. You should take a step back and consider the bigger picture.

Upvotes: 0

Galik
Galik

Reputation: 48625

What you are doing looks like manually recreating the function overload system. Let the compiler take the strain:

void someFunc(some_known_type_1 bar);
void someFunc(some_known_type_2 bar);
void someFunc(some_known_type_3 bar);

template < class T, T success> 
void foo(T check) {

    someFunc(check);

    // ...
}

Upvotes: 2

Related Questions