G_glop
G_glop

Reputation: 73

Overloading functions both on parameter and return types

I know the trick of using a struct for return type overloading:

struct function {
  operator typeA () { return overloadForTypeA(); }
  operator typeB () { return overloadForTypeB(); }
}

Problem is that this disables parameters and parameter type overloading. So i've been trying to extend this concept using templates:

struct function {
    //template on the constructor so types are inffered from arguments
    template<typename... Args>
    funct(Args... arguments) { myArguments = arguments; }

    //intermediate storeage of arguments, problem is the template doesn't extend to here
    Args myArguments;

    //overloads for return type int
    int overloadForInt(char* chr) { return 20; }
    int overloadForInt(int i) { return 4; }

    //overloads for return type char
    char overloadForChar(char* chr) { return 'c'; }
    char overloadForChar(int i) { return 'i'; }

    //implcit cast operators to archive return type overloading
    operator int() { return overloadForInt(myArguments...); } //if myArguments doesn't match any overload of overloadForInt compile error should happen
    operator char() { return overloadForChar(myArguments...); }
}

As you see, i ran into the problem of the template not extending to rest of the stuct. Is there a way to extend the constructor template over the whole struct to solve this particular problem? Or is there another way to archive return type being overlodeable while keeping parameters and parameter type overloading?

Upvotes: 1

Views: 128

Answers (1)

linuxfever
linuxfever

Reputation: 3823

The implicit cast operators can potentially call your overloadForXXX with more than argument, which would result in a compilation error. In the following I will make the assumption that you are simply interested in calling the overloads with each argument you pass in the constructor. Please note the use of the Boost Fusion library. The idea is to make your function class a class template and then use a helper function to create that class which will infer the types of arguments for you.

#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <iostream>



template <class... Args>
class function {

  // using a tuple to store the arguments
  std::tuple<Args...> m_args;

public:
  function(Args... args)
: m_args(std::forward<Args>(args)...){}

private:
  struct call_for_int {

    void overload(char* chr) {
      std::cout << "Called Int overload with char" << std::endl;
    }

    void overload(int chr) {
      std::cout << "Called Int overload with int" << std::endl;
    }

    template <class T>
    void operator()(T&& t) {
      overload(std::forward<T>(t));
     }
   };

  struct call_for_char {

    void overload(char* chr) {
      std::cout << "Called Char overload with char" << std::endl;
    }

    void overload(int chr) {
      std::cout << "Called Char overload with int" << std::endl;
    }

    template <class T>
    void operator()(T&& t) {
      overload(std::forward<T>(t));
    }
  };  

public:
  // use this to call the char overloads
  void call_char() {
    auto fun = call_for_char();
    boost::fusion::for_each(m_args, std::ref(fun));
  }

  // use this to call the int overloads
  void call_int() {
    auto fun = call_for_int();
    boost::fusion::for_each(m_args, std::ref(fun));
  }
};

// helper function to infer the passed arguments
template <class... Args>
auto make_function(Args&&... args) {
  return function<Args...>(std::forward<Args>(args)...);
}

int main() {
  auto f = make_function(4, 2, 42);
  f.call_char();
  f.call_int();
}

Upvotes: 1

Related Questions