3

I want to know if there is an approach to decrease the number of overloaded function (function edit) in the below code.

 class foo
  {
  public:
      foo(int _a, char _b, float _c) : a(_a), b(_b), c(_c){};
      void edit(int new_a);
      void edit(char new_b);
      void edit(float new_c);
      void edit(int new_a, char new_b);
      void edit(int new_a, float new_c);
      void edit(char new_b, float new_c);
      void edit(int new_a, char new_b, float new_c);
      void info();
  private:
     int   a;
     char  b;
     float c;
  };

Here is the implementation of the edit functions :

void foo::edit(int new_a)
{
    a = new_a;
}
void foo::edit(char new_b)
{
    b = new_b;
}
void foo::edit(float new_c)
{
    c = new_c;
}
void foo::edit(int new_a, char new_b)
{
    a = new_a;
    b = new_b;
}
void foo::edit(int new_a, float new_c)
{
    a = new_a;
    c = new_c;
}
void foo::edit(char new_b, float new_c)
{
    b = new_b;
    c = new_c;
}
void foo::edit(int new_a, char new_b, float new_c)
{
    a = new_a;
    b = new_b;
    c = new_c;
}

The edit function will let the user change the parameters of the object as he wishes. But the thing is that if we add a new parameter we have to add to many overloaded function and I thought there should be a better way.

Here with 3 parameters we need 7 overloaded functions but if we had 4 parameters (a, b, c and d) then we had to develop 14 overloaded function! That's why I think there should be a better approach.

Thanks.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
meysamimani
  • 315
  • 2
  • 12
  • You can make it a *member function template* but unless you show us the implementation of these `edit` member functions we can't say/advise anything with certainty. – Jason Feb 01 '22 at 15:34
  • Imagine that foo class is a contacts manager and each object of this class contains parameters like (name, phone,email) and the edit function aims to change these parameters, but you might not want to change all parameters, maybe you need to change (phone and name) or just (phone) or just (name) so you have different options to change the parameters. You see what I mean? – meysamimani Feb 01 '22 at 15:38
  • let edit return a reference to `this` so you can chain them: `f.edit(1).edit(1.0);` then you do not need the combinatorial overloads. If setting more than one value needs a check that depends on both parameters then the same approach, but `edit` returns some proxy that does the checking – 463035818_is_not_an_ai Feb 01 '22 at 15:40
  • If I understand your comment-explanation of this code, any call to `edit()` with multiple parameters has the same effect as multiple calls to `edit()`, each with one parameter? If that's correct, [edit] your question to better describe the problem you are solving. – Drew Dormann Feb 01 '22 at 15:40
  • void foo::edit(int new_a) { a = new_a; } – meysamimani Feb 01 '22 at 15:41
  • 1
    @meysamimani please [edit] your question to be complete. – Drew Dormann Feb 01 '22 at 15:41
  • void foo::edit(char new_b) { b = new_b; } – meysamimani Feb 01 '22 at 15:42
  • The two previous comments are implementation of the function, other overloaded functions follow the same task. That means, whatever the arguments are, the function will initialize the data member with the arguments of edit function. – meysamimani Feb 01 '22 at 15:45
  • Adding `private: template void edit(T) = delete; template void edit(T, U) = delete; template void edit(T, U, V) = delete;` can help *disallow* other types from implicit conversion. – Eljay Feb 01 '22 at 15:47
  • I have updated my question by adding implementation of the edit functions. – meysamimani Feb 01 '22 at 15:49
  • why do you think you need that combinatoric explosion? Why the caller cannot call `edit(1)` and then `edit(1.0f)` instead of `edit(1,1.0f)` ? If you add one member you need to add one setter, its not clear why you want all combinations – 463035818_is_not_an_ai Feb 01 '22 at 15:50
  • I'm going to vote to reopen this. There is a particularly elegant approach using variadic templates and `constexpr if`. Hint: solve the single argument template version first, then move onto the variadic case (2 and 3 arguments). The Boost developers (www.boost.org) are good at this sort of thing. – Bathsheba Feb 01 '22 at 15:52
  • Dear @463035818_is_not_a_number. As you said, I can have two times calling the edit function instead of having two function that one change the int parameter and other change int and float for instance. But I was thinking that it would be easier for the user of these class to use one time the edit parameter and just provide the parameters he wants to change. – meysamimani Feb 01 '22 at 15:54
  • And upvoted for having the instinct that there's a better way. That's a hallmark of a good programmer. You can't teach that. – Bathsheba Feb 01 '22 at 15:54
  • Related to [how-to-generate-all-the-permutations-of-function-overloads](https://stackoverflow.com/questions/30561407/c-how-to-generate-all-the-permutations-of-function-overloads). – Jarod42 Feb 01 '22 at 16:02

4 Answers4

7

With variadic and (ab)using std::get<T> on std::tuple, you might do

template <typename... Ts>
void edit(Ts... values)
{
    ((std::get<Ts&>(std::tie(a, b, c)) = std::get<Ts&>(std::tie(values...))), ...);
}

Demo.

Note: I use std::get<Ts&>(std::tie(values...)) instead of simply values to get error with duplicated input types(edit(42, 42);).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
3

You can avoid the huge number of overloads and still allow the caller to set more than one member in a single expression:

class foo
  {
  public:
      foo(int _a, char _b, float _c) : a(_a), b(_b), c(_c){};
      foo& edit(int new_a) { a = new_a; return *this;}
      foo& edit(char new_b) { b = new_b; return *this; }
      foo& edit(float new_c) { c = new_c; return *this; }
      
  private:
     int   a;
     char  b;
     float c;
};

int main() {
    foo f(1,'c',2.0);
    f.edit(42).edit(42.0f).edit('a');
}

Adding a member requires you to write one overload rather than N to support all combinations.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1

Given your edit functions that modify a single member:

void edit(int new_a)
{
    a = new_a;
}
void edit(char new_b)
{
    b = new_b;
}
void edit(float new_c)
{
    c = new_c;
}

You can define a single function in C++11 using variadic templates to support multiple parameters in terms of multiple calls with a single parameter:

template< typename FirstType, typename ...OtherTypes >
void edit(FirstType ft, OtherTypes ...ot)
{
    edit(ft);
    edit(ot...);
}

Using C++17, fold expressions can make this function even simpler.

template< typename ...Types >
void edit(Types ...types)
{
    (edit(types), ...);
}

Note: This solution will not try to prevent multiple changes to the same type, such as edit(1, 2, 3);

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
1

The previous solutions are quite fine, but suppose that all elements have a different type.

A possibility is to still use a variadic template, and in the call to indicate with a string which element must be modified.
This would allow the possibility to have the same type for different elements.

#include <iostream>
#include <string>

class foo {
  public:
      foo(int _a, char _b, float _c) : a(_a), b(_b), c(_c){};
      void edit() {};
      template<typename T1, typename... T2>
      void edit (const std::string& id, T1 var1, T2... var2) {
          if (id == "a") a = var1;
          else if (id == "b") b = var1;
          else if (id == "c") c = var1;
          edit(var2...);
      };
      void info();
  //private:
     int   a;
     char  b;
     float c;
};

  std::ostream& operator<<(std::ostream& os, const foo& obj) {
    std::cout << "a = " << obj.a <<  " b = " << obj.b << " c = " << obj.c;
    return os;
  }

int main() {
    foo example(1, 'a', 2.0);
    example.edit("c", 3.0f, "b", 'g', "a", 5);
    std::cout << example << std::endl;
}
Damien
  • 4,809
  • 4
  • 15
  • 20