Is there a way to dynamically change the return-type of a function in C++ based on function parameter values?

Viewed 142

I am working on a problem that requires me to return different return-types based on my function parameter values that I provide.

I want to do something like this --

In the code below, doSomething() is an already existing function (used by a lot of clients) which takes mode as a function parameter, and returns std::list<ReturnType> already.

Based on the mode value, I had to create another sub-functionality which returns a shared_future<std::list<ReturnType>>.

How can I change this code so that it can return one of the two return types based on the mode value?

Note: ReturnType is a template typename which we are using for the entire class.

Code:

    std::shared_future<std::list<ReturnType> > futureValue() {
        return functionReturningSharedFuture();
    }
    
    std::list<ReturnType> listValue() {
        return functionReturningList();
    }
    std::list<ReturnType> doSomething(int mode) {
        if(mode == 1){
            // new functionality that I added
            return futureValue(); // This (obviously) errors out as of now
        }
        else{
            // already there previously
            return listValue();
        }
    }
    
    int main() {
        doSomething(1);
        return 0;
    }

How can I change this code so that it can return one of the two return types based on the mode value?

Constraints and Issues:

  1. This issue could've been easily solved by function overloading if we provide an extra function parameter (like a true value), but that extra argument is not useful, since we are already using mode. Also, it isn't considered a good design to add variables which have almost no use.
  2. One of the major constraints is that there are clients who are already using this doSomething() expect a std::list<ReturnType>, and so I cannot return boost::any or std::variant or anything similar.
  3. I tried using std::enable_if, but it wasn't working out since we are getting the mode value at runtime.
  4. We can't use template metaprogramming since that would change the way our function is being called on the client-side. Something that we can't afford to do.

Thank you.

2 Answers

This cannot be done.

You can only have one function with a given signature. If you have calling code that already expects this to return a std::list<ReturnType>, that's it; you're done.

If you could guarantee that all existing calling code looks like

auto l = obj.doSomething(1);

then you could potentially change the return type to something which would look like a std::list to any calling code. But if there's any calling code that looks like

std::list<ReturnType> l = obj.doSomething(1);

then that's off the table.

You probably need to rethink your design here.

From the example main, I see doSomething(1);, so maybe at the call site the value of the parameter mode is always known at compile-time. In this case, one option is that you make doSomething a template<int mode> function. I'm thinking about something like this:

#include <iostream>
#include <list>
#include <vector>

// assuming you cannot change this (actually you have changed it in you example, ...)
std::list<int> doSomething(int mode) {
    std::cout << "already existing function\n";
    return std::list<int>{1,2,3};
}

// then you can put this too
template<int N>
auto doSomething();

template<>
auto doSomething<10>() {
    std::cout << "new function\n";
    return std::vector<int>{1,2,3};
}

int main() {
    auto x = doSomething(3);
    auto y = doSomething<10>();
}

Probably another option would be to use a if constexpr intead of if and an auto/decltype(auto) return type in doSomething, but I haven't tried it.

Related