Josh Johnson
Josh Johnson

Reputation: 563

C++ same function parameters with different return type

I need to find some way to mock an overload of a function return type in C++.

I know that there isn't a way to do that directly, but I'm hoping there's some out-of-the-box way around it. We're creating an API for users to work under, and they'll be passing in a data string that retrieves a value based on the string information. Those values are different types. In essence, we would like to let them do:

int = RetrieveValue(dataString1);
double = RetrieveValue(dataString2);
// Obviously, since they don't know the type, they wouldn't use int =.... It would be:
AnotherFunction(RetrieveValue(dataString1)); // param of type int
AnotherFunction(RetrieveValue(dataString2)); // param of type double

But that doesn't work in C++ (obviously). Right now, we're having it set up so that they call:

int = RetrieveValueInt(dataString1);
double = RetrieveValueDouble(dataString2);

However, we don't want them to need to know what the type of their data string is.

Unfortunately, we're not allowed to use external libraries, so no using Boost.

Are there any ways we can get around this?

Just to clarify, I understand that C++ can't natively do it. But there must be some way to get around it. For example, I thought about doing RetrieveValue(dataString1, GetType(dataString1)). That doesn't really fix anything, because GetType also can only have one return type. But I need something like that.

I understand that this question has been asked before, but in a different sense. I can't use any of the obvious answers. I need something completely out-of-the-box for it to be useful to me, which was not the case with any of the answers in the other question asked.

Upvotes: 14

Views: 56270

Answers (10)

Carlos Davalillo
Carlos Davalillo

Reputation: 13

The answer is simple just declare the function returning void* type and in the definition return a reference to the variable of different types. For instance in the header (.h) declare

void* RetrieveValue(string dataString1);

And in the definition (.cpp) just write

void* RetrieveValue(string dataString1)
{
    if(dataString1.size()<9)
    {
        static double value1=(double)dataString1.size();
        return &value1;
    }
    else
    {
        static string value2=dataString1+"some string";
        return &value2;
    }
}

Then in the code calling RetrieveValue just cast to the right value

string str;
string str_value;
double dbl_value;
if(is_string)
{
    str_value=*static_cast<*string>(RetrieveValue(str));
}
else
{
    dbl_value=*static_cast<*double>(RetrieveValue(str));
}

Upvotes: 0

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361302

You've to start with this:

template<typename T>
T RetrieveValue(std::string key)
{
     //get value and convert into T and return it
}

To support this function, you've to work a bit more, in order to convert the value into the type T. One easy way to convert value could be this:

template<typename T>
T RetrieveValue(std::string key)
{
     //get value
      std::string value = get_value(key, etc);

      std::stringstream ss(value);
      T convertedValue;
      if ( ss >> convertedValue ) return convertedValue;
      else throw std::runtime_error("conversion failed");
}

Note that you still have to call this function as:

int x = RetrieveValue<int>(key);

You could avoid mentioning int twice, if you could do this instead:

Value RetrieveValue(std::string key)
{
     //get value
      std::string value = get_value(key, etc);
      return { value };
}

where Value is implemented as:

struct Value
{
    std::string _value;

    template<typename T>
    operator T() const   //implicitly convert into T
    {
       std::stringstream ss(_value);
       T convertedValue;
       if ( ss >> convertedValue ) return convertedValue;
       else throw std::runtime_error("conversion failed");
    }
}

Then you could write this:

int    x = RetrieveValue(key1);
double y = RetrieveValue(key2);

which is which you want, right?

Upvotes: 33

Walter
Walter

Reputation: 45414

If the datastrings are compile-time constants (as said in answering my comment), you could use some template magic to do the job. An even simpler option is to not use strings at all but some data types which allow you then to overload on argument.

struct retrieve_int {} as_int;
struct retrieve_double {} as_double;

int RetrieveValue(retrieve_int) { return 3; }
double RetrieveValue(retrieve_double) { return 7.0; }

auto x = RetrieveValue(as_int);    // x is int
auto y = RetrieveValue(as_double); // y is double

Upvotes: 1

Simon
Simon

Reputation: 10841

As an alternative to the template solution, you can have the function return a reference or a pointer to a class, then create subclasses of that class to contain the different data types that you'd like to return. RetrieveValue would then return a reference to the appropriate subclass.

That would then let the user pass the returned object to other functions without knowing which subclass it belonged to.

The problem in this case would then become one of memory management -- choosing which function allocates the returned object and which function deletes it, and when, in such a way that we avoid memory leaks.

Upvotes: 0

Seth Carnegie
Seth Carnegie

Reputation: 75130

Since you used an example that wasn't really what you wanted, you threw everyone off a bit.

The setup you really have (calling a function with the return value of this function whose return type is unknowable) will not work because function calls are resolved at compile time.

You are then restricted to a runtime solution. I recommend the visitor pattern, and you'll have to change your design substantially to allow for this change. There isn't really another way to do it that I can see.

Upvotes: -2

user283145
user283145

Reputation:

The only sane way to do this is to move the return value to the parameters.

 void retrieve_value(std::string s, double& p);
 void retrieve_value(std::string s, int& p);
 <...>

 double x;
 retrieve_value(data_string1, x);

 int y;
 retrieve_value(data_string2, y);

Upvotes: 3

Robᵩ
Robᵩ

Reputation: 168616

Whether it is an overload or a specialization, you'll need the information to be in the function signature. You could pass the variable in as an unused 2nd argument:

int RetrieveValue(const std::string& s, const int&) {
  return atoi(s.c_str());
}
double RetrieveValue(const std::string& s, const double&) {
  return atof(s.c_str());
}

int i = RetrieveValue(dataString1, i);
double d = RetrieveValue(dataString2, d);

Upvotes: 2

HackyStack
HackyStack

Reputation: 5157

If you know your value can never be something like zero or negative, just return a struct holding int and double and zero out the one you don't need...

It's a cheap and dirty, but easy way...

struct MyStruct{
int myInt;
double myDouble;
};

MyStruct MyFunction(){
}

Upvotes: 1

int a=itoa(retrieveValue(dataString));
double a=ftoa(retrieveValue(dataString));

both return a string.

Upvotes: 0

Twiltie
Twiltie

Reputation: 572

Unfortunately there is no way to overload the function return type see this answer Overloading by return type

Upvotes: 0

Related Questions