c++typecheckingtemplate-function

Checking Datatypes of Template Function


I'm trying to make a function based on input Template. It reads a file.

template<class size> void config::readConfig(char * setting, char * subsetting, size & status) {
    char temp[255];
    if (!GetPrivateProfileStringA(setting, subsetting, nullptr, temp, 255, cfg)) {
        error = true;
    }
    if (std::is_same<size, char [4]>::value) {
        sprintf_s(status, "%s", temp);
    } else { status = atof(temp); }
}

I'm basically just checking if the desired input is a char. If it is, then we'll copy the char read over, but if not, we'll copy the bool/int/float/double. Maybe I'm just using std::is_same incorrectly.

My code won't compile because it doesn't look like it recognizes the check and seems like it always returns true.


Solution

  • Your code won't compile because an if statement is a runtime construct. Consider this code:

    int foo(bool b)
    {
        if (b)
        {
            // attempt to call a function that does not exist
            function_that_does_not_exist();
        }
        else
        {
            return 1;
        }
    }
    

    You can talk to me all day about how b is always false, but the compiler still requires the code in the true case of the if block to compile. What's happening in your example for something like an int is this:

    template<>
    void config::readConfig(char* setting, char* subsetting, int& status) {
        char temp[255];
        if (!GetPrivateProfileStringA(setting, subsetting, nullptr, temp, 255, cfg)) {
            error = true;
        }
    
        // if (std::is_same<size, char [4]>::value) {
        if (false) {
            sprintf_s(status, "%s", temp);
        } else { status = atof(temp); }
    }
    

    The compiler does not know how to make sprintf_s compile when status is an int.

    The solution is to use overloading:

    template<class size>
    void config::readConfig(char * setting, char * subsetting, size & status) {
        char temp[255];
        if (!GetPrivateProfileStringA(setting, subsetting, nullptr, temp, 255, cfg)) {
            error = true;
        }
    
        status = atof(temp);
    }
    
    template<size_t N>
    void config::readConfig(char* setting, char* subsetting, char (&status)[N]) {
        char temp[255];
        if (!GetPrivateProfileStringA(setting, subsetting, nullptr, temp, 255, cfg)) {
            error = true;
        }
    
        sprintf_s(status, "%s", temp);
    }
    

    The thing you're looking for is usually referred to as static_if (similar to the D conditional compilation construct of a "Static If Condition"). There is no support in C++17 and it isn't planned for C++2a (yet), but you can emulate it easily with the answer to this question.

    #include <type_traits>
    
    template <typename T, typename F>
    auto static_if(std::true_type, T t, F f) { return t; }
    
    template <typename T, typename F>
    auto static_if(std::false_type, T t, F f) { return f; }
    
    template <bool B, typename T, typename F>
    auto static_if(T t, F f) { return static_if(std::integral_constant<bool, B>{}, t, f); }
    
    template <class size>
    void config::readConfig(char* setting, char* subsetting, size& status)
    {
        char temp[255];
        if (!GetPrivateProfileStringA(setting, subsetting, nullptr, temp, 255, cfg))
        {
            error = true;
        }
    
        static_if<std::is_same<size, char [4]>::value>
        (
            [&] (auto& status) { sprintf_s(status, "%s", temp); },
            [&] (auto& status) { status = atof(temp); }
        )(status);
    }