Home > Back-end >  Is it possible to union variable conversion function?
Is it possible to union variable conversion function?

Time:05-02

For example, there are three variable conversion functions.

//Int
int toInt(std::string input)
{
    int ret = strtol(input.c_str(), 0, 10);
    return ret;
}

//Double
double toDouble(std::string input)
{
    double ret = strtod(input.c_str(), 0);
    return ret;
}

//Const char*
const char* toChar(std::string input)
{
    return input.c_str();
}

I want to combine these functions like below

~~ toConvert(std::string input)
{
    if ( Variable type is Int )
        return strtol(~~~)
    else if ( Varibale type is Double )
        return strtod(~~~)
    ...
// Using
int    i = toConvert<int>(input);
double d = toConvert<double>(input);
const char* c = toConvert<const char*>(input);

Is it possible?

Please help for implemet above function.

CodePudding user response:

Your "using" code is passing a template argument to toConvert(), so make sure toConvert() is actually a template, and then you can specialize it for each type you want, eg:

template<typename T>
T toConvert(std::string &input) { return T{}; /* or, throw an exception... */ }

template<>
int toConvert<int>(std::string &input)
{
    return strtol(input.c_str(), 0, 10);
}

template<>
double toConvert<double>(std::string &input)
{
    return strtod(input.c_str(), 0);
}

template<>
const char* toConvert<const char*>(std::string &input)
{
    return input.c_str();
}

Or, if you are using C 17 or later, you can instead use if constexpr in a single template:

#include <type_traits>

template<typename T>
T toConvert(std::string &input)
{
    if constexpr (std::is_same_v<T, int>)
        return strtol(input.c_str(), 0, 10);
    else if constexpr (std::is_same_v<T, double>)
        return strtod(input.c_str(), 0);
    else if constexpr (std::is_same_v<T, const char*>)
        return input.c_str();
    else
        return T{}; // or, throw an exception, or static_assert a compiler error...
}

Notice in either case that I changed the input parameter to std::string&. Your original code was taking in the std::string by value, which means it takes in a copy of the caller's string, and so the const char* conversion would return a dangling pointer to invalid memory when the copied std::string is freed upon return. By taking a reference instead, you avoid that copy.

You might be tempted to take in a const std::string& reference instead, but that would allow calls like toConvert<const char*>("string") to compile but still return a dangling pointer, since the compiler would have to create a temporary std::string to bind to the const reference. But a string literal can't bind to a non-const reference.

CodePudding user response:

That's possible using C 17's if constexpr syntax. For example:

template <typename T>
constexpr bool dependent_false = false;

template <typename T>
T convertTo(const std::string& input)
{
    if constexpr (std::is_same_v<T, int>) {
        return std::stoi(input);
    } else if constexpr (std::is_same_v<T, double>) {
        return std::stod(input);
    } else {
        static_assert(dependent_false<T>, "Can't convert to the specified type");
    }
}

Live Demo

The whole dependent_false thing exists to make the static_assertion dependent on the template parameter so that it only gets checked when the function template is instantiated. Just an odd quirk of the C template rules.


Note that I left the const char* case out. It has very different semantics from the others, since the pointer returned by c_str points to memory owned by the std::string object.

  • Related