0

I have a function that has a lot of parameters. (4-7 parameters)

For simplicity, this is an example:-

class B{
    friend class C;
    int f(int param1,float param2, structA a, structB b){
         //... some code ...
    }
    //.... other functions ....
};

Sometimes, I want to encapsulate it under another (more-public) function that has the same signature:-

class C{
    B* b;
    public: int g(int param1,float param2, structA a, structB b){
        return b->f(param1,param2,a,b);
    }
    //.... other functions ....
};

In my opinion, the above code is :-

  • tedious
  • causes a bit of maintainability problem
  • human error-prone

Is there any C++ technique / magic / design-pattern to assist it?

In the real case, it happens mostly in edge-cases that composition is just a little more suitable than inheritance.

I feel that <...> might solve my problem, but it requires template from which I want to avoid.

javaLover
  • 6,347
  • 2
  • 22
  • 67

2 Answers2

1

but it requires template from which I want to avoid.

That's, in my opinion, the wrong mindset to have. You should avoid templates if you have a very good reason to do so, otherwise you should embrace them - they are a core feature of the C++ language.

With a variadic template, you can create a perfect-forwarding wrapper as follows:

class C{
    B* b;
public: 

    template <typename... Ts>
    int g(Ts&&... xs){
        return b->f(std::forward<Ts>(xs)...);
    }

};

The above g function template will accept any number of arguments and call b->f by perfectly-forwarding them.

(Using std::forward allows your wrapper to properly retain the value category of the passed expressions when invoking the wrapper. In short, this means that no unnecessary copies/moves will be made and that references will be correctly passed as such.)

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • (sad face) With template, I can't move it to .cpp -> compile time increase & other bad side effects. – javaLover Feb 28 '17 at 09:29
  • 1
    @javaLover: unless your code is heavily-templated and full of compile-time metaprogramming the impact of adding some wrapper templates will be **unnoticeable** in terms of compile-time. Please clarify *"other bad side effects"*. Also clarify *"I can't control which function I want to expose"*. – Vittorio Romeo Feb 28 '17 at 09:31
  • I can't control which function I want to expose <--- I am wrong (please forget it) – javaLover Feb 28 '17 at 09:31
  • other bad side effects = circular dependency (it is not so much problem) ..... Sometimes, I encapsulate for pimpl, so in that case, all will be lose. (sorry, I didn't mention it explicitly) – javaLover Feb 28 '17 at 09:33
  • ...do you really need *PImpl* here? It [has its own drawbacks](http://en.cppreference.com/w/cpp/language/pimpl). I might be wrong, but to me it sounds like you're unnecessarily trying hard to avoid templates and anything that *could* increase compilation time. That can be reasonable, but: **have you measured the impact of adding a template on compilation time?** If not, you should, and then draw your conclusions. Also, **PImpl** is trading run-time for compilation-time - there is some overhead as seen in the page I linked. – Vittorio Romeo Feb 28 '17 at 09:37
  • There are some workaround to not sacrifice run-time by sacrificing a bit of maintainability .... http://stackoverflow.com/questions/4921932/pimpl-idiom-without-using-dynamic-memory-allocation and http://stackoverflow.com/questions/33769057/heap-free-pimpl-incorrect-or-superstition?rq=1 . (I didn't test it yet.) Good point about "you should measure", thank. – javaLover Feb 28 '17 at 09:40
  • @javaLover If you are pimpl wrapping, then the definition of `f` isn't visible in the header; `g` cannot be dependent on `f`'s definition if you are hiding `f` at the point of `g`'s definition. – Yakk - Adam Nevraumont Feb 28 '17 at 14:24
  • One case where the template cannot be used for this : if the function use a forward declared class. – Adrian B. Dec 15 '20 at 22:11
1

In a public header:

using f_sig = int(int param1,float param2, structA a, structB b);

class hidden;
class famous {
  hidden* pImpl
public:
  f_sig g;
};

In your .cpp:

class hidden {
  friend class famous;
  f_sig f;
};

Now, you cannot use this pattern to define what f or g does, but this does declair their signatures. And if your definition doesn't match the declaration you get an error.

int hidden::f(int param1,float param2, structA a, structB b) {
  std::cout << "f!";
}
int famous::g(int param1,float param2, structA a, structB b) {
  return pImpl->f(param1, param2, a, b);
}

type the signatures wrong above, and you'll get a compile-time error.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524