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.
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);
}
...