Errors when using C ++ function templates

This is my first post here. Feels uncomfortable in reality; -)

I am struggling with C ++ templates. I have a class 'textSettings"

that reads TXT files for value pairs, each one on a line, and puts them in a std :: map that uses a std :: string for key and value.

Possible inputs to a text file are, for example, GRID_SIZE 6

so that the map key becomes a string "GRID_SIZE"

and the value is a string "6"

.

In my program I want to use them as a type, in fact it's meaning: int/float/bool/string

.

The program only expects these 4 types to be entered as input. So I use a templated function to find a specific value in the map via my key and instantiate only those types.

In this function I check the type using std::tr1::is_same

to parse the value correctly.

It doesn't work in the else clause where Xcode complains, throwing three errors. It cannot use val for float or int or bool. All errors look the same:

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

However, all types of complaints are already being processed by other proposals if

. I really don't know how to deal with it. I am still learning C ++ and templates, so I might be ignoring something obvious. If I comment out the line, the program compiles and links fine. Obviously I cannot parse the values ​​from the text file, then ...

I am 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

+3


source to share


4 answers


You are getting this error because for instances other than std::string

, you are assigning it->second

which is equal std::string

to the referenced variable val

that is not assigned for the string type ( float

, int

or bool

).

The fact that your condition std::tr1::is_same<T, std::string>::value

will evaluate as false

does not mean that you are not required to provide lexically correct code inside the conditional block.



This type of problem would be better addressed by creating a separate function overload for each required type and using an overloaded function call to automatically match the required:

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

      

+2


source


Problem

In each instance of a template, there is only one branch if

that is semantically correct and for which the compiler is able to generate proper instance code, while all the others are simply wrong.

Take a look at the following code, the error you are making is just the same:

{
    int a = 33; // Assume this code was into a template and 'int = T'
    if(std::is_same<decltype(a), int>::value)
        a = 44; // Okay
    else
        a = "hello"; // How can this compile?  
}

      



Since you are explicitly creating a templated function for all these types, the code is actually generated and thus you get errors.

Decision

There are several solutions, but specializing your function can be good (remember: functions cannot be partially specialized)

template<typename T>
bool textSettings::findValue(const std::string &key, T& val) {
    // type not recognized, assert or handle
    ...
    return false;
}

template<> // Specialized for int
bool textSettings::findValue<int>(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;
}
// all the others..

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&);

      

+1


source


I think you misunderstood the concept of using trait types. The type characteristics are evaluated as a constant expression at compile time and replaced with a constant value that is evaluated at run time. So in your example for T=int

the below equivalent template instance 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()) {
        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 error correcting string is still compiled, but on its own 'in the context of the type int

, its an invalid expression where the string is assigned to an integer.

type properties can be useful during template evaluation, but in this particular case, template specialization is best used to select 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;
}

      

+1


source


You need to use a solution based on what's called tagging . The most popular example is the std :: advance implementation. Here's an example. The problem with your code is that the compiler will try to instantiate your function for any given type T

. Imagine you are calling a function with T = int

. Then replace everything T

with int

. Will your function compile? No, because there val

will be int

, and you will try to assign std::string

to int

and there is no implicit conversion.

Here is your live code : All you need is the following trick (see the link for submitting tags below):

bool dispatcher(const std::string& str, int& val) {
    val = atoi(str.c_str());
    return true;
}

bool dispatcher(const std::string& str, float& val) {
    val = atof(str.c_str());
    return true;
}

bool dispatcher(const std::string& str, bool& val) {
    val = static_cast<bool>(atoi(str.c_str()));
    return true;
}

bool dispatcher(const std::string& str, std::string& val) {
    val = str;
    return true;
}

bool dispatcher(const std::string& str, ...) {
    printf("Textsettings:: Error, type unknown!\n");
    return false;
}


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

      

+1


source







All Articles