Question

I was wondering if it is possible to change the return type of a function based on the type of variable it is being assigned to. Here's a quick example of what I mean.

I want to create a function that parses a variable of int, bool, or float from a string. For example...

Int value = parse("37");
Float value = parse("3.14");
Bool value = parse("true");

I understand if I make this function a template, that the variable type must be determined from the argument list which is always going to be a string. Is there any other way of doing this with c++?

Was it helpful?

Solution

This can be done with a conversion function

struct proxy {
    string str;
    proxy(string const &str):str(str) { }
    template<typename T> operator T() { 
        return boost::lexical_cast<T>(str); 
    }
};

proxy parse(string const &str) { return proxy(str); }

Now you just need to do

float a = parse("3.1");

And it should work well. Incidentally, you may just use the class directly. I recommend renaming it to conversion_proxy to point to the fact that it's just a proxy to a happening conversion but that it itself doesn't do conversion

struct conversion_proxy {
    string str;
    conversion_proxy(string const &str):str(str) { }
    template<typename T> operator T() { 
        return boost::lexical_cast<T>(str); 
    }
};

float a = conversion_proxy("3.1"); 

OTHER TIPS

I can't tell from your question if you know this or not, but you can indeed do this with a template. The only catch is that you will have to specify the type that you are converting from in each invocation instead of relying on inference (since as you said the argument type will always be the same).

template<typename T> T parse(const string& str) { /* do stuff for other types */ }
template<> int parse<int>(const string& str) { /* do stuff for ints */ }
template<> double parse<double>(const string& str) { /* do stuff for doubles */ }
template<> bool parse<bool>(const string& str) { /* do stuff for bools */ }
// etc.

And then invoke as

int value = parse<int>("37");
double value = parse<double>("3.14");
bool value = parse<bool>("true");

If you already knew this just ignore this answer, but it's not clear from your question that you are aware that this is possible.

Of course, if what you're doing isn't really generic (and so you have to specialize for each type you want to parse) then writing a template isn't the right thing to do anyway.

By the way, you can do it pretty generically with a single function like this (assuming parse is what you really want to do):

#include <sstream>
template<typename T> T parse(const string& str) 
{
  T t;
  std::istringstream sstr(str);
  sstr >> t;
  return t;
}

This will work for any default-constructable, stream-extractable type, which includes all built-ins.

You could pass your output argument as a pointer or reference.

Like this:

template<class T> void parse(const std::string &input, T& output);

Then code like this:

double d; parse(input, d);
int i; parse(input, i);

should work.

However, your code looks like a perfect fit for an std::istringstream that would just be:

istringstream is(input);
input >> d;

If you have somewhat complicated formatting involved, a trick I have had pretty good luck with involves creating custom objects with custom operator>> that pulls out data.

Then it could be like:

istringstring is(input);
input >> LineExtracter(x, y, d);

I would agree with litb who was a little quicker than myself. Use the casting operators.

#include <iostream>
#include <string>
#include <sstream>

class Convertible
{
public:
    int m_Integer;
    bool m_Bool;
    double m_Double;

    Convertible() : m_Integer(0), m_Bool(false), m_Double(0.0) {};

    operator int() const
    {
        return m_Integer;
    }
    operator bool() const
    {
        return m_Bool;
    }
    operator double() const
    {
        return m_Double;
    }
};

Convertible parse(std::string data)
{
    Convertible l_result;

    std::istringstream converter(data);
    converter >> l_result.m_Integer;

    std::istringstream converter2(data);
    converter2 >> l_result.m_Bool;

    std::istringstream converter3(data);
    converter3 >> l_result.m_Double;

    return l_result;
}

void main()
{
    int l_convertedInt = parse("2");
    bool l_convertedBool = parse("true");
    double l_convertedDouble = parse("3.14");

    std::cout << "Converted '2' to " << l_convertedInt << std::endl;
    std::cout << "Converted 'true' to " << l_convertedBool << std::endl;
    std::cout << "Converted '3.14' to " << l_convertedDouble << std::endl;
}

Unfortunately, that isn't possible. In C++ it is not possible to overload a function based on it's return value. You either have to have 3 functions, ParseInt, ParseFloat, and ParseBool, or use a function template.

You could return void* and then cast the result as needed.

I advise against this though. C++ is a strongly typed language. The advantage of this is that the compiler can catch errors earlier than a dynamically typed language.

No this type of behavior is not possible in C++. To be allowable it would necessitate being able to define functions of the same name at the same scope that differed only by return type. This is not legal in C++.

C++ can do some return type specialization such as covariant return types on overridden virtual functions. But it does not support what you are looking for.

Here's my adaptation of @Tyler McHenry's answer for my situation where the argument to parse() is a type other than a string.

Note that I found I had to introduce a template specialization in order to avoid a type conversion warning (float to int).

(Also see live demo.)

#include <iostream>

struct MyUnion
{
public:
  union {
    bool bool_value;
    int int_value;
    float float_value;
  };
};

template<typename T> T parse(const MyUnion& h)
{
  T t;

  if (typeid(T) == typeid(bool)) {
    t = h.bool_value;
  } else if (typeid(T) == typeid(int)) {
    t = h.int_value;
  } else if (typeid(T) == typeid(float)) {
    // t = h.float_value; // see **Warning** below; use float specialization instead
  }

  return t;
}

// 'float' template specialization to avoid conversion warning.
template<> float parse(const MyUnion& h)
{
  return h.float_value;
}

int main()
{
  MyUnion mu1; mu1.bool_value = true;
  MyUnion mu2; mu2.int_value = 42;
  MyUnion mu3; mu3.float_value = 3.14159;

  std::cout << "As bool: "  << parse<bool>(mu1)  << std::endl;
  std::cout << "As int: "   << parse<int>(mu2)   << std::endl;
  std::cout << "As float: " << parse<float>(mu3) << std::endl;
}

// **Warning**
// In function 'T parse(const Heterogeneous&) [with T = int]':
// Line 22: warning: converting to 'int' from 'const float'
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top