9

Anybody think there are advantages to using a class or struct to pass arguments ?

Like instead of

f(int,float,string)

Have

f(Args)

Where Args is struct with int,float,string members.

Advantage is easy to create multiple default parameters and not have to change function signature when new arguments added.

0

7 Answers 7

5

The obvious benefit would be to have logical grouping of semantically related data items.

Once you do, add some (member) operations on the structure that will guarantee your invariants.

The encapsulation raises the abstraction level of your code and this makes it easier to maintain/reason about.

See also Law Of Demeter

Sign up to request clarification or add additional context in comments.

Comments

4

I think the great advantage is not having to rely to parameter order. Rely on order is error prone if you are changing frequently the interface, instead if you change the parameter struct you are always explicitly assigning values to a member variable which has a specific semantic.

Take for example Direct3D11 ID3D11Device::CreateDepthStencilState function: passing a const D3D11_DEPTH_STENCIL_DESC *pDepthStencilDescis a lot more clear than asking for all the parameter it require.

Moreover think about modifiability: you don't need to change this method signature but only the underlying data structure during refactoring. I've found this especially useful when working collaboratively, where someone specify the interface and someone else have to implement it.

Comments

3

Anybody think there are advantages to using a class or struct to pass arguments ?

Yes, I think there's a lot of advantages.

Having large parameter lists on functions will distract client code from semantical parameter consistency, which can be better managed within an appropriate struct or class.

Also it's more flexible to use a struct, if additional (possibly optional) parameters need to be added later.

Comments

3

Doing the devil advocate here. There are also drawbacks here, mostly semantical. The first and foremost, it will require a lot of code if some of those args are passed by reference, another one by constant reference, the third one by const pointer and the forth by value. Would require you to explicitly write move constructor and default constructor for the argument struct, which will quickly become tedious. It would also be tedious to add members to that struct.

2 Comments

Anything cannot be achieved by std::reference_wrapper?
Also "It would also be tedious to add members to that struct." requires the same effort for adding an argument to a function.
2

I also think using a struct is better: You can circumvent the hustle with parameter types and order. Suppose you have foo(A, B) (for types A and B). But still foo(b, a) might compile, depending on implicit constructions etc.

This concept can also be generalized using some kind of Context classes. Relying on C++11 variadic templates you could pass a "parameter superset context" to a subset one.

Comments

2

Whether you use one argument contained in a class/struct or multiple arguments depends on the meanings of the arguments.

Bad use of a struct:

struct Foo
{
   char const* source;
   char* destination;
};


Foo strcpy(Foo foo);

Good use of a struct:

struct Point
{
   int x;
   int y;
};


int distanceFromOrigin(Point p) { ... }

instead of

int distanceFromOrigin(int x, int y) { ... }

Comments

0

Using a struct to replace the parameter list of a function "f()" makes sense when:

  1. You pass all the arguments by value, or you easily could.
  2. The struct itself can be a simple aggregate (i.e. supports designated ".member = value" initializer lists in c++20)
  3. Your current function signature has many parameters of the same type, so using designated initializers w/ a struct would be way clearer code for callers.

It's an even better choice when you also have:

  1. 1 or more defaulted or optional parameters, which can become std::optional<> and defaulted fields of the struct. Especially if you have to handle some of these cases by overloading "f()".
  2. You can identify other places in the code where this same struct is useful.

It is a BAD idea when your function "f()" has any of these properties:

  1. "inout" parameters you can't easily refactor away.
  2. Parameters that either can't be copied or are expensive to copy, since using a simple aggregate struct means you'd be copying them all at every call site. Note that plain old pointers ARE cheap to copy, so this really means const reference parameters like large chunks of text in a const std::string&.

For example, it makes sense to change this function to take a struct:

bool setVersion(int major, int minor, int patch, int build=0);

Becomes:

struct Version{
   int major; 
   int minor;
   int patch;
   int build;
}
bool setVersion(Version ver);

And a caller can now do (c++20):

setVersion({.major = 5, .minor = 1, .patch = 2, .build = 101}); 

which is basically named parameters, even though C++ doesn't technically have that feature.

However, for something like this it makes zero sense to turn that parameter list into a struct:

int parseToInt(const std::string& str, int defaultValue, bool& successful);

You've only got 3 parameters, which are totally distinct types (so the compiler WILL catch out-of-order params at call sites) and one is an 'inout' return. Converting that parameter list into a struct is just going to make a bad situation way, way worse for both your callers & performance.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.