views:

883

answers:

7

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++?

A: 

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.

Grant Limberg
Technically true, but you can achieve much the same thing with user-defined conversions -- see litb's excellent answer.
j_random_hacker
A: 

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.

Matt Brunell
Use boost:variant or boost::any over a void*.
GMan
Neither would work, actually. The code returning the void* has no idea whether the void* is going to be casted to a float* or int*. Boost::variant is safer, because it will fail in a well-defined matter,but it suffers from the same crystal ball problem.
MSalters
+7  A: 

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.

Tyler McHenry
Martin Cote
Which is why I showed that it can be done with templates, then noted that you don't want to use templates if it's not in fact generic, then showed how it could be generic (for many types).
Tyler McHenry
+1 I think this is probably the best way to go. parse<type>(string). In fact in older code, before I used boost:lexical_cast (which is what you should use if you end up doing something like this answer), I usually had a from_string<> function just like that.
GMan
+1 - This is the better way to define for this problem.
Ganesh M
Thanks for the response. I didn't realise you could do this. It doesn't seem an aweful lot different than just making the 3 seperate functions, but it's still a handy piece of info to know. I originally tried to implement this method, but I couldn't quite figure it out.
Morgan
+1. Although it is possible to avoid specifying the return type in any way via user-defined conversion functions (see litb's answer), your solution has a definite readability advantage. Your parse<T>() function is essentially Boost's lexical_cast<T>() :)
j_random_hacker
A: 

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.

JaredPar
Technically it's true that you can't overload by return type in C++, but you can achieve much the same thing with user-defined conversions -- see litb's excellent answer.
j_random_hacker
+4  A: 

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);
Zan Lynx
+17  A: 

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");
Johannes Schaub - litb
This looks a bit wrong. you are not calling the () operator, just the ctor. I'm not downvoting because I'm not sure. Please clarify.
the_drow
i didn't intend to call op() :) i called a function which returned a proxy. In the second part i now propose using the class directly, without an intervening function in between
Johannes Schaub - litb
Ah, this is really clever >:| And drow, there is no () operator. There is, however, an implicit conversion to T. Great answer, litb.
GMan
thanks GMan, appreciated :)
Johannes Schaub - litb
This is exactly the kind of answer I was looking for! Thanks so much. I must admit though, I'm not 100% sure I fully understand exactly how it works. But it works exactly how I was hoping it would work. Thanks!
Morgan
+1 Beautiful and simple :)
Magnus Skog
+1, for teaching me something I did not know. That is a really nice piece of code.
rcollyer
+2  A: 

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