1

So I have come across a problem where I have a function with multiple parameters. Out of usability I started overloading it so that for instace I can pass a std::string instead of a std::fstream instance. In this overload a new std::fsteam will be constructed from that string and then the other function will be called. Like this:

void func(const std::string & filename) {
  std::fstream file(filename);

  func(file)
}

void func(std::fstream & file) {
  // ...
}

This all works fine. But when you start doing this for more than one parameter or more than 2 possible types everything starts becoming a mess and you might have to write lots of overloads with duplicate code etc.
So I was wondering if there was a more elegant solution to this problem for as many parameters as you need.

I know this problem is not specific to C++ but I'm interested in solutions for the problem in C++.

BrainStone
  • 3,028
  • 6
  • 32
  • 59
  • 3
    Have you heard of templates? – NathanOliver Nov 07 '16 at 13:05
  • Yes. However I'm not sure how I could use them in this specific case. – BrainStone Nov 07 '16 at 13:06
  • 3
    You could specialize templates and you could also specify conditions for certain types with [type traits](http://en.cppreference.com/w/cpp/header/type_traits). – wally Nov 07 '16 at 13:07
  • Make the parameter a template, (try to) construct an `fstream` from that, pass it on. Why does that not work for you? – nwp Nov 07 '16 at 13:08
  • @nwp I really want to limit the parameters to very specifc types. And just trying to construct an ``fstream`` from it will eventually cause problems. I also use a fair amount of different types including some of my own. – BrainStone Nov 07 '16 at 13:09
  • @flatmouse I'll have a look at it. I did not know about type traits. – BrainStone Nov 07 '16 at 13:10
  • 3
    [Related](http://stackoverflow.com/questions/874298/c-templates-that-accept-only-certain-types/874337), but it mostly says "Don't worry about it". The problems caused by passing the wrong types are a compilation error, which is pretty much the best thing that can happen when you do something wrong in C++. – nwp Nov 07 '16 at 13:17

2 Answers2

2

One possible solution is to use helper classes:

class param1 {

public:
    param1(const std::string & filename)
          : filename(filename), file(nullptr)
    {
    }

    param1(std::fstream &file) : file(&file)
    {
    }

    std::string filename;
    std::fstream *file;
};

class param2 {

public:
    param2(int flag);
    param2(some_particular_class &classptr);

    // etc...
};

void func(const param1 &p1, const param2 &p2);

A helper class for each parameter contains overloaded constructors for each possible type that can be passed in. You end up with a single func() to implement.

Within each helper class you'll need to figure out how to correctly store each parameter, which is going to be a different question.

With C++14 you could also make use of std::variant here, as well.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Using helper classes seems a little excessive. – BrainStone Nov 07 '16 at 13:41
  • Not when you have four or five parameters, and every parameter can be any available alternative. The choices are, either: dozens, if not hundreds, of overloaded functions; template-generated code bloat; or this. Choose one. – Sam Varshavchik Nov 07 '16 at 14:12
  • In the latter case I agree. It's really a matter of preference and circumstance I'd say. – BrainStone Nov 07 '16 at 14:41
1

So I found a pretty decent and expandable solution:

Let's assume we have a function that accepts 3 paramters of the type type_right1, type_right2 and type_right3 but we also want to provide it with the additional overloads for the respective types type_right1, type_wrong2 and type_wrong3. We also asume to get from type_wrongX to type_rightX we just call the constructor of the latter and pass the former. The code looks like this:

template<class T1, class T2>
void func(type_wrong1 arg1, T1 arg2, T2 arg3) {
  func(type_right1(arg1), arg2, arg3);
}

template<class T1>
void func(type_right1 arg1, type_wrong2 arg2, T1 arg3) {
  func(arg1, type_right2(arg2), arg3);
}

void func(type_right1 arg1, type_right2 arg2, type_wrong3 arg3) {
  func(arg1, arg2, type_right2(arg3));
}

void func(type_right1 arg1, type_right2 arg2, type_right3 arg3) {
  // actual function
}

This solution can be easily expanded with both more parameters and more than two types per parameter and requires a lot less writing and also just generates the functions you actually need!
Thank you for your help.

BrainStone
  • 3,028
  • 6
  • 32
  • 59