Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting errors when using C++ function templates

This is my first post here. Feels kind of awkward actually ;-)

I'm struggling with C++ templates. I have a class 'textSettings" that reads TXT files for value pairs, each on it's line, and puts them in std::map that uses std::string for both key and value.

Possible entrances in the text file could be for instance GRID_SIZE 6, so that the key of the map gets to be the string "GRID_SIZE" and the value is the string "6".

In my program I want to use them as the type the value actually is: int/float/bool/string.

The program expects only to get these 4 types as input. I therefore use a templated function to find a certain value in the map through its key, and only instantiate it for these types.

In this function I'm checking the type using std::tr1::is_same to be able to parse the value correctly.

It fails at the else clause, where XCode complains, throwing three errors. It can't cast val to float or int or bool. The errors all look the same:

/src/textSettings.cpp:82:17: Assigning to 'int' from incompatible type 'std::string' (aka 'basic_string') /src/textSettings.cpp:82:17: Assigning to 'float' from incompatible type 'std::string' (aka 'basic_string') /src/textSettings.cpp:82:17: Assigning to 'bool' from incompatible type 'std::string' (aka 'basic_string')

However all types complained about are already handled by the other if clauses. I really don't know how to deal with this. I'm still learning about C++ and templates, so I might be overlooking something obvious. If I comment the line out, the program compiles and links perfectly. Obviously I can't parse values from the text file then...

I'm using XCode 5.1.1

textSettings.h

template<typename T>
bool findValue(const std::string& key, T& val);

textSetting.cpp

template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
    std::map<std::string, std::string>::iterator it;
    it = data.find(key);
    if (it != data.end()) {
        if (std::tr1::is_same<T, int>::value) {
            val = atoi(it->second.c_str());
        }
        else if (std::tr1::is_same<T, float>::value) {
            val = atof(it->second.c_str());
        }
        else if (std::tr1::is_same<T, bool>::value) {
            val = static_cast<bool>(atoi(it->second.c_str()));
        }
        else if (std::tr1::is_same<T, std::string>::value) {
            val = it->second; // <- ERROR HERE
        }
        else {
            printf("Textsettings:: Error, type unknown!\n");
            return false;
        }
        return true;
    }
    return false;
}
template bool textSettings::findValue<int>(const std::string&, int&);
template bool textSettings::findValue<float>(const std::string&, float&);
template bool textSettings::findValue<bool>(const std::string&, bool&);
template bool textSettings::findValue<std::string>(const std::string&, std::string&);

Thanks for your comments

like image 768
Vincent Jacobs Avatar asked Mar 03 '26 10:03

Vincent Jacobs


2 Answers

You are getting this error, because, for instantiations other than std::string, you are assigning it->second, which is std::string to the variable referenced by val which is of non-assignable to string type (float, int or bool).

The fact that your condition std::tr1::is_same<T, std::string>::value will evaluate to false does not mean that you are not obliged to provide lexically correct code inside the conditional block.

This type of problem will be better solved by creating separate function overload for each needed type and use overloaded function invocation to automatically match the desired one:

bool ParseStr(const std::string& str, int& val) {
   val = atoi(str.c_str());
   return true;
}
bool ParseStr(const std::string& str, float& val) {
   val = atof(str.c_str());
   return true;
}
bool ParseStr(const std::string& str, bool& val) {
   val = static_cast<bool>(atoi(str.c_str()));
   return true;
}
bool ParseStr(const std::string& str, std::string& val) {
   val = str;
   return true;
}
template <typename T>
bool ParseStr(const std::string& str, T& val) {
   printf("Textsettings:: Error, type unknown!\n");
   return false;
}

template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
  std::map<std::string, std::string>::iterator it;
  it = data.find(key);
  if (it != data.end()) {
     return ParseStr(it->second, val);
  }
  return false;
 }
like image 197
Krizz Avatar answered Mar 04 '26 22:03

Krizz


I think you mis-understood the concept of type traits usage. The type traits are evaluated as a constant expression during compile time and is replaced by a constant value that is evaluated during runtime. So in your example for T=int, the following is the equivalent template instance code being evaluated

bool textSettings::findValue(const std::string &key, int& val) {
    std::map<std::string, std::string>::iterator it;
    it = data.find(key);
    if (it != data.end()) {
        if (true) {
            val = atoi(it->second.c_str());
        }
        else if (false) {
            val = atof(it->second.c_str());
        }
        else if (false) {
            val = static_cast<bool>(atoi(it->second.c_str()));
        }
        else if (false) {
            val = it->second; // <- ERROR HERE
        }
        else {
            printf("Textsettings:: Error, type unknown!\n");
            return false;
        }
        return true;
    }
    return false;
}

As you can see, the line being error-ed out is still being compiled but per se' in the context of type int, its an invalid expression where a string is being assigned to an integer.

type-traits may be helpful during template evaluation, but in this particular case, template specialization is best to use for selection of code based on type.

template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
    printf("Textsettings:: Error, type unknown!\n");
    return false;
}

template<>
bool textSettings::findValue(const std::string &key, std::string& val) {
    std::map<std::string, std::string>::iterator it;
    it = data.find(key);
    if (it != data.end()) {
        val = it->second; // <- ERROR HERE
        return true;
    }
    return false;
}

template<>
bool textSettings::findValue(const std::string &key, int& val) {
    std::map<std::string, std::string>::iterator it;
    it = data.find(key);
    if (it != data.end()) {
        val = atoi(it->second.c_str());
        return true;
    }
    return false;
}

template<>
bool textSettings::findValue(const std::string &key, float& val) {
    std::map<std::string, std::string>::iterator it;
    it = data.find(key);
    if (it != data.end()) {
        val = atof(it->second.c_str());
        return true;
    }
    return false;
}

template<>
bool textSettings::findValue(const std::string &key, bool& val) {
    std::map<std::string, std::string>::iterator it;
    it = data.find(key);
    if (it != data.end()) {
        val = static_cast<bool>(atoi(it->second.c_str()));
        return true;
    }
    return false;
}
like image 44
Abhijit Avatar answered Mar 04 '26 22:03

Abhijit



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!