S_M
S_M

Reputation: 85

Converting between types in function templates

I have a function template that gives the result by reference variable that defined by template. This function must convert a value and assign to the reference variable. I have problem with compiling in the line that I show in the code.

What is wrong and how can I solve that?

The following is my code:

#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>

using namespace std;

map<wstring, wstring> my_data = {
    { L"data1", L"123.456"  },
    { L"data2", L"2213.323" },
    { L"data3", L"3321.321" },
    { L"data4", L"1000" },
    { L"data5", L"2000" }
};



template<class T>
bool get_map_value(const wstring &map_key, T& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    if (typeid(T).name() == typeid(wstring).name())
        map_value = tmp->second;
        //Error C2440   '=': cannot convert from 'std::wstring' to 'int'    
        //Error C2440   '=': cannot convert from 'std::wstring' to 'double' 

    else if (typeid(T).name() == typeid(double).name())
        map_value = _wtof(tmp->second.c_str());

    else if (typeid(T).name() == typeid(int).name())
        map_value = _wtoi(tmp->second.c_str());


    return true;
}


int main() {

    double d = 0.0;
    wstring w = L"";
    int i = 0;

    get_map_value(L"data1", w);
    get_map_value(L"data3", d);
    get_map_value(L"data4", i);


    return 0;
}

Upvotes: 1

Views: 94

Answers (3)

eerorika
eerorika

Reputation: 238311

What is wrong

The type of tmp->second is wstring. When the template is instantiated with T = int, then the type of map_value is int&. The line

map_value = tmp->second;

Has pretty self explanatory error message

Error C2440   '=': cannot convert from 'std::wstring' to 'int'

std::wstring is not implicitly convertible to int. That is the problem. You must remember that the entire function must be well formed, even if execution cannot reach some part of it.


how can I solve that?

What you need is just a few overloads. We shall refactor the differing part of the function, so that you don't need to repeat the identical part.

parse_value(int& variable, const std::wstring& value) {
    variable = _wtoi(tmp->second.c_str());
}
parse_value(double& variable, const std::wstring& value) {
    variable = _wtof(tmp->second.c_str());
}
parse_value(std::wstring& variable, const std::wstring& value) {
    variable = value;
}

template<class T>
bool get_map_value(const wstring &map_key, T& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    parse_value(map_value, tmp->second);

    return true;

}

Upvotes: 2

Rerito
Rerito

Reputation: 6086

First, remove these ugly typeid()s... And use SFINAE instead.

template <typename T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
bool get_map_value(std::wstring const& map_key, T& map_value) {
    auto const val = std::find(my_data.cbegin(), my_data.cend(), map_key);
    if (val != std::cend(my_data)) {
        map_value = _wtof(val.second);
        return true;
    } else {
        return false;
    }
}

Similarly, you then add another definition for integral types:

template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
bool get_map_value(std::wstring const& map_key, T& map_value) {
    auto const val = std::find(my_data.cbegin(), my_data.cend(), map_key);
    if (val != std::cend(my_data)) {
        map_value = _wtoi(val.second);
        return true;
    } else {
        return false;
    }
}

And finally a simple overload for std::wstring:

bool get_map_value(std::wstring const& map_key, std::wstring& map_value) {
    auto const val = std::find(my_data.cbegin(), my_data.cend(), map_key);
    if (val != std::cend(my_data)) {
        map_value = val.second;
        return true;
    } else {
        return false;
    }
}

Upvotes: 0

Rakete1111
Rakete1111

Reputation: 48938

Template are evaluated at compile time. Run-time if statements don't protect the code being run, as the compiler can't guarantee that the path won't be taken.

As of C++17 you would be able to use constexpr if, but meanwhile, you have to write different specializations for it (or even overloads, you don't even need a template):

bool get_map_value(const wstring &map_key, wstring& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    map_value = tmp->second;

    return true;
}

bool get_map_value(const wstring &map_key, double& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    map_value = _wtof(tmp->second.c_str());    

    return true;
}

bool get_map_value(const wstring &map_key, int& map_value)
{
    auto tmp = my_data.find(map_key);

    if (tmp == my_data.end())
        return false;

    map_value = _wtoi(tmp->second.c_str());

    return true;
}

Upvotes: 0

Related Questions