views:

157

answers:

8

I've recently started working on developing APIs written in C. I see some subroutines which expect 8(Eight) parameters and to me it looks ugly and cumbersome passing 8 parameters while calling that particular subroutine. I was wondering if something more acceptable and cleaner way could be implemented .

+2  A: 

8 could be a proper number. or it could be that many of those 8 should all belong to a proper class as members, then you could pass a single instance of the class... hard to tell just by this kind of high level discussion.

edit: in c - classes would be similar to structures in this case.

Randy
C doesn't have classes.
Amardeep
meh - just noticed 'c' - so the class thing might earn me some negative comments.
Randy
but you scattered an idea: going OOP is not bad... or borrow something from the OO world in plain C, too, could be a good idea, depending on the actual problem of the user. -- something like what Eli Bendersky already explained in his answer
ShinTakezou
@ShinTakezou: I was unlucky enough to have to work a few years in a project where OOP was extensively used in a C program. It was a nightmare, would not recommend.
PauliL
@PauliL it depends on how much it is "OO" and how it OO-like features are borrowed. I dislike a lot OOP, but some ideas are useful and can be taken painlessly in C, without syntactic sugar of course. Moreover, "object" is a rather broad term. Having an "opaque" object, handled by proper functions, and to be passed each time to these functions, is a "pattern" used, and powerful (just an example, cairo context... without, a lot of function should have a lot more of args (and indeed the whole stuffs would have been harder to implement in general))
ShinTakezou
+4  A: 

Large numbers of arguments in a function call are usually showing up a design problem. There are ways of apparently reducing the number of parameters, by such means as creating structures which are passed instead of individual variables or having global variables. I'd recommend you DON'T do either of these things, and attend to the design. No quick or easy fix there, but the people who have to maintain the code will thank you for it.

Brian Hooper
Thanx Brian, This not being a low level API and also the project being agile, will implement structure and ask the user to fill in the member details.
kumar
+1, Right answer. I tried to elaborate a bit in my own answer. It was too long for a comment.
T.E.D.
Kumar, perhaps I didn't make myself clear; I recommend you don't do that. I would suggest you clear up you design to avoid passing masses of parameters; passing a structure to conceal the large number of parameters will only end in tears.Example: what parameters does the following call have...int do_foo (struct foo_params *params){ /* do something */}
Brian Hooper
A: 

If the API seems cumbersome with that many parameters, use a pointer to a structure to pass parameters that are related in some way.

I'm not going to judge your design without seeing it firsthand. There are legitimate functions that require a large number of parameters, for example:

  1. Filters with a large number of polynomial coefficients.
  2. Color space transforms with sub-pixel masks and LUT indices.
  3. Geometric arithmetic for n-dimensional irregular polygons.

There are also some very poor designs that lead to large parameter prototypes. Share more information about your design if you seek more germane responses.

Amardeep
+6  A: 

If a number of arguments can be logically grouped together you may consider creating a structure containing them and simply pass that structure as an argument. For example instead of passing two coordinate values x and y you could pass a POINT structure instead.

But if such a grouping isn't applicable, then any number of arguments should be fine if you really need them, although it might be a sign that your function does a little too much and that you could spread work over more, but smaller functions.

Joey
Amardeep, Johannes, I think it may be unwise to suggest this, in case the functions end up with parameters like struct bucket {int this, char *that, float the_other }, as I have suffered from my former colleagues doing for this reason on previous projects. It's a bit of a pet peeve of mine, so I hope you'll forgive me from banging on about it.
Brian Hooper
@Brian Hooper: Your concerns are valid. When giving advice it is always risky since the advisee is free to do something horrible with whatever you offer. I think that is why both suggestions advised that the groupings be logical or related in some way.
Amardeep
In C++ I might agree. In C, doing so is often more of a PITA than it is worth. If he has to spend 8 lines of code before and after the call to pack and unpack the structure, what has he gained?
T.E.D.
@T.E.D. It is easy to make generalizations like that. I would want to see his code first.
Amardeep
@T.E.D. using a struct in the API would in turn encourage the use of that struct in the client code. This way one would get around packing and unpacking. OTOH I don't recall myself using the Win32 structs for anything else than making calls to that API...
mxp
@T.E.D. In C99, you can use an immediate anonymous structure right in the call if you don't want to spend LoC packing/unpacking the structure.
R..
A: 

One pattern used by some APIs (like pthreads) is "attribute objects" that are passed into functions instead of a bunch of discrete arguments. These attribute objects are opaque structures, with functions for creating, destroying, modifying and querying them. All in all this takes more code than simply dumping 10 arguments to a function, but it's a more robust approach. Sometimes a bit of extra code that can make your code much more understandable is worth the effort.

Once again, for a good example of this pattern, see the pthreads API. I wrote a lengthy article on the design of the pthreads API a couple of months ago, and this is one of the aspects I addressed in it.

Eli Bendersky
A: 

It also seems to be interesting to consider this question not from the standpoint of ugliness/not ugliness but from the point of performance.

I know that there are some x86 calling conventions that can use registers for passing two first arguments and stack for all other arguments. So I think that if one use this type of calling convention and always use a pointer to structure to pass arguments in situations when a function needs more then 2 parameters in overall the function call might be faster. On Itanium registers are always used for passing parameters to a function.

I think it might be worth testing.

skwllsp
it is worth testing even to know which conventions are in use, even though I think the "classical" "cdecl" is widely used also outside x86 world (it was e.g. used for C implementations I've seen on Amiga, were the OS API uses registers, and avoiding the use of pragmas(to specify registers to use) meant to have wrappers that took args from the stack and moved them into registers to call this or that system function.
ShinTakezou
+3  A: 

Yes, 8 is almost certianly too much.

Here's some old-school software engineering terms for you. Cohesion and coupling. Cohesion is how well a subroutine holds together on its own, and coupling is how clean the interfaces between your routines are (or how self-sufficient your routines are).

With coupling, generally the looser the better. Interfacing only through parameters ("data coupling") is good low coupling, while using global variables ("common coupling") is very high coupling. When you have a high number of parameters, what is usually the case is that someone has tried to hide their common coupling with a thin veneer of data coupling. Its bad design with a paint job.

With cohesion, the higher (more cohesive) the better. Any routine that modifies eight different things is also quite likey to suffer from low cohesion. I'd have to see the code to see for sure, but I'd be willing to bet that it would be very difficult to clearly explain what that routine does in a short sentence. Sight unseen, I'd guess it is temporally cohesive (just a bunch of stuff that needs to be done at roughly the same time).

T.E.D.
I would recommend "Structured Design" by Yourdon and Constantine as the book to read on this subject. It is very old, but very wise.
Brian Hooper
+1  A: 

I suggest the usage of structures too. Maybe you want to rethink your APIs design.

Remember that your APIs will be used by developers, and it would be hard to use an 8 parameter function call.

thesp0nge
Exactly, that's what made me uneasy.
kumar