views:

494

answers:

6

If you code in C and configure your compiler to insist that all functions are declared before they are used (or if you code in C++), then you can end up with one of (at least) two organizations for your source files.

Either:

  • Headers
  • Forward declarations of (static) functions in this file
  • External functions (primary entry points)
  • Static - non-public - functions

Or:

  • Headers
  • Static - non-public - functions
  • External functions (primary entry points)

I recognize that in C++, the term 'static' is not preferred, but I'm primarily a C programmer and the equivalent concept exists in C++, namely functions in an anonymous namespace within the file.

Question:

  • Which organization do you use, and why do you prefer it?

For reference, my own code uses the second format so that the static functions are defined before they are used, so that there is no need to both declare them and define them, which saves on having the information about the function interfaces written out twice - which, in turn, reduces (marginally) the overhead when an internal interface needs to change. The downside to that is that the first functions defined in the file are the lowest-level routines - the ones that are called by functions defined later in the file - so rather than having the most important code at the top, it is nearer the bottom of the file. How much does it matter to you?

I assume that all externally accessible functions are declared in headers, and that this form of repetition is necessary - I don't think that should be controversial.

+3  A: 

Number 2 for me.

I think using static or other methods to make your module functions and variables private to the module is a good practice.

I prefer to have my api functions at the bottom of the module. Conversely I put the api functions at the top of my classes as classes are generally reusable. Putting the api functions at the top make it easier to find them quickly. Most IDEs, can take you to any function pretty directly.

EvilTeach
+6  A: 

I've always used method #1, the reason being that I like to be able to quickly tell which functions are defined in a particular file and see their signatures all in one place. I don't find the argument of having to change the prototypes along with the function definition particularly convincing since you usually wind up changing all the code that calls the changed functions anyway, changing the function prototypes while you are at it seems relatively trivial.

Robert Gamble
that's my view on this too. and in any case - if you forget changing the declaration, you will get a compiler error, and if you forget to change the definition, you will get a linker error.
Johannes Schaub - litb
+2  A: 

(Talking about C code)

Number 2 for me because I always forget to update forward decls to reflect static functions changes.

But I think that the best practice should be

  • headers
  • forward declarations + comment on function behaviour for each one
  • exported functions + eventual comments about implementation details when code is not clear enough
  • static functions + eventual comments about implementation details
rjack
+2  A: 

Number 2: because I write many short functions and refactor them freely, it'd be a significant nuisance to maintain forward declarations. If there's an Emacs extension that does that for you with no fuss, I'd be interested, since the top-down organization is a bit more readable. (I prefer top-down in e.g. Python.)

Actually not quite your Number 2, because I generally group related functions together in the .c regardless of whether they're public or private. If I want to see all the public declarations I'll look in the header.

Darius Bacon
+3  A: 

In C code I use a simple rule:

  • Every C file with non-static members will have a corresponding header file defining those members.

This has worked really well for me in the past - makes it easy enough to find the definition of a function because it's in the same-named .h file if I need to look it up. It also works well with doxygen (my preferred tool) because all the cruft is kept in the header where I don't spend most of my time - the C file is full of code.

For static members in a file I insist in ordering the declarations in such a way that they are defined by instantiation before use anyway. And, I avoid circular dependency in function calls almost all of the time.

For C++ code I tried the following:

  • All code defined in the header file. Use #pragma interface/#pragma implementation to inform the compiler of that; kind of the same way templates put all the code in the header.

That's worked really well for me in C++. It means you end up with HUGE header files which can increase compile time in some cases. You also end up with a C++ body file where you simply include the header and compile. You can instantiate your static member variables here. It also became a nightmare because it was far too easy to change your method params and break your code.

I moved to

  • Header file with doxygen comments (except for templates, where code must be included in the header) and full body file, except for short methods which I know I'd prefer be inlined when used.

Separating out implementation from definition has the distinct plus that it's harder to change your method/function signatures so you're less likely to do it and break things. It also means that I can have huge doxygen blocks in the header file documenting how things work and work in the code relatively interruption free except for useful comments like "declare a variable called i" (tongue in cheek).

Ada forces the convention and the file naming scheme on you. Most dynamic languages like Ruby, Python, etc don't generally care where/if you declare things.

Adam Hawes
A: 

How much does it matter to you?

It's not.

It is important that all local function will be marked as static, but for my opinion defining how to group function in the file is too much. There is no strong reasoning for any version and i don't find any strong disadvantage ever.

In general coding convention is very important and we trying to define as much as possible, but in this case my feeling, that this is unjustified overhead.

After reading all posts again it seems like i should simply upvote (which i did) Darius answer, instead writing all of these ...

Ilya