c++templatestypeinfo

In c++ can you separate the function called based on the template?


I have a function in C++ that works, in theory. What I mean by this, is if the function was converted to a language like Python, it would work just fine because it is not pre-compiled. However, in a language like C++, it throws errors.

This is the function:

template <typename T>
T  JSONValue::get(){
    if(detect_number::detect<T> == true){
        if(type == JSONValueType::Number)
            return (T)number.value();
    }
    else if(detect_string::detect<T> == true){
        if(type == JSONValueType::String){return string.value();}
        return deparse(*this);
    }
    else if(detect_chararray::detect<T> == true){
        if(type == JSONValueType::String){return string.value().c_str();}
        std::string dep = deparse(*this);
        return dep.c_str();
    }
    else if(detect_boolean::detect<T> == true){
        if(!(type == JSONValueType::Boolean))
            return false;
        return boolean.value();
    }
    else if(is_specialization<T, std::vector>::value){
        if(type != JSONValueType::Array){
            std::cerr<<"must be an array to do this"<<std::endl;
            return T{};
        }
        T output;
        for(int i = 0; i < size(); i++){
        output.push_back(array.value().at(i).get<std::decay<decltype(*output.begin())>::type>());   
        }
        return output;
    }
    return T(0);
}

In the if statements, it detects the type, T which is passed. If it is a number, do this, all through if it is a vector then do this.

The problem is, if a vector is put in as the template parameter, it throws an error because a number cannot be casted to a vector, and so on.

Is there a way to either get the compiler to ignore the error, or fix it so that the compiler does not have to ignore it and it will run?

I remember seeing something a while back that if the type is something in the typename, then run this function, otherwise run this other function.


Solution

  • In C++17 and later, you can use if constexpr, eg:

    if constexpr (detect_number::detect<T>){
        // T is a numeric type, do numerical things here...
    }
    else if constexpr (detect_string::detect<T>){
        // T is a string type, do stringy things here...
    }
    ...
    

    if statements are evaluated at runtime, so the entire function has to be syntaxically correct at compile-time, even though only parts of it will actually be used.

    But, if constexpr statements are evaluated at compile-time instead. As such, any unused code blocks will be eliminated by the compiler and not evaluated at all. That will allow you use T in different ways in different if constexpr blocks. The compiler will treat your code as if you had written it more like this:

    // only when T is a number
    template <typename T>
    T JSONValue::get(){
        if(type == JSONValueType::Number)
            return (T)number.value();
        }
        return T(0);
    }
    
    // only when T is a string
    template <typename T>
    T JSONValue::get(){
        if(type == JSONValueType::String){return string.value();}
        return deparse(*this);
    }
    
    ...
    

    Prior to C++17, you would have to use SFINAE or template specialization to accomplish the same thing, eg:

    // SFINAE 
    
    template <typename T,
        typename std::enable_if<detect_number::detect<T>, bool>::type = true
    >
    T JSONValue::get(){
        if(type == JSONValueType::Number)
            return (T)number.value();
        return T(0);
    }
    
    template <typename T,
        typename std::enable_if<detect_string::detect<T>, bool>::type = true
    >
    T JSONValue::get(){
        if(type == JSONValueType::String){return string.value();}
        return deparse(*this);
    }
    
    ...
    
    // specialization
    
    template <typename T>
    T JSONValue::get(){
        return T{};
    }
    
    template<>
    int JSONValue::get<int>(){
        if(type == JSONValueType::Number)
            return (T)number.value();
        return T(0);
    }
    
    template<>
    std::string JSONValue::get<std::string>(){
        if(type == JSONValueType::String){return string.value();}
        return deparse(*this);
    }
    
    ...