Should I put many functions into one file? Or, more or less, one function per file?
Asked Answered
P

9

9

I love to organize my code, so ideally I want one class per file or, when I have non-member functions, one function per file.

The reasons are:

  1. When I read the code I will always know in what file I should find a certain function or class.

  2. If it's one class or one non-member function per header file, then I won't include a whole mess when I include a header file.

  3. If I make a small change in a function then only that function will have to be recompiled.

However, splitting everything up into many header and many implementation files can considerately slow down compilation. In my project, most functions access a certain number of templated other library functions. So that code will be compiled over and over, once for each implementation file. Compiling my whole project currently takes 45 minutes or so on one machine. There are about 50 object files, and each one uses the same expensive-to-compile headers.

Maybe, is it acceptable to have one class (or non-member function) per header file, but putting the implementations of many or all of these functions into one implementation file, like in the following example?

// foo.h
void foo(int n);

// bar.h
void bar(double d);

// foobar.cpp
#include <vector>
void foo(int n) { std::vector<int> v; ... }
void bar(double d) { std::vector<int> w; ... }

Again, the advantage would be that I can include just the foo function or just the bar function, and compilation of the whole project will be faster because foobar.cpp is one file, so the std::vector<int> (which is just an example here for some other expensive-to-compile templated construction) has to be compiled in only once, as opposed to twice if I compiled a foo.cpp and bar.cpp separately. Of course, my reason (3) above is not valid for this scenario: After just changing foo(){...} I have to recompile the whole, potentially big, file foobar.cpp.

I'm curious what your opinions are!

Partheniaparthenocarpy answered 10/2, 2009 at 5:20 Comment(0)
M
12

IMHO, you should combine items into logical groupings and create your files based on that.

When I'm writing functions, there are often a half a dozen or so that are tightly related to each other. I tend to put them together in a single header and implementation file.

When I write classes, I usually limit myself to one heavyweight class per header and implementation file. I might add in some convenience functions or tiny helper classes.

If I find that an implementation file is thousands of lines long, that's usually a sign that there's too much there and I need to break it up.

Matthei answered 10/2, 2009 at 5:25 Comment(0)
T
8

One function per file could get messy in my opinion. Imagine if POSIX and ANSI C headers were made the same way.

#include <strlen.h>
#include <strcpy.h>
#include <strncpy.h>
#include <strchr.h>
#include <strstr.h>
#include <malloc.h>
#include <calloc.h>
#include <free.h>
#include <printf.h>
#include <fprintf.h>
#include <vpritnf.h>
#include <snprintf.h>

One class per file is a good idea though.

Tammeratammi answered 10/2, 2009 at 5:34 Comment(1)
Sounds pretty great to me, never again have to look up what header a function is in... but they could also have convenience headers that included all of the string functions in string.h and so forth. On the other hand compilers could make assumptions about including "strlen.h" if strlen is used (with a warning of course)Lamprey
S
5

We use the principle of one external function per file. However, within this file there may be several other "helper" functions in unnamed namespaces that are used to implement that function.

In our experience, contrary to some other comments, this has had two main benefits. The first is build times are faster as modules only need to be rebuilt when their specific APIs are modified. The second advantage is that by using a common naming scheme, it is never necessary to spend time searching for the header that contains the function you wish to call:

// getShapeColor.h
Color getShapeColor(Shape);

// getTextColor.h
Color getTextColor(Text);

I disagree that the standard library is a good example for not using one (external) function per file. Standard libraries never change and have well defined interfaces and so neither of the points above apply to them.

That being said, even in the case of the standard library there are some potential benefits in splitting out the individual functions. The first is that compilers could generate a helpful warning when unsafe versions of functions are used, e.g. strcpy vs. strncpy, in a similar way to how g++ used to warn for inclusion of <iostream.h> vs. <iostream>.

Another advantage is that I would no longer be caught out by including memory when I want to use memmove!

Semolina answered 10/2, 2009 at 10:50 Comment(0)
O
5

One function per file has a technical advantage if you're making a static library (which I guess it's one of the reasons why projects like the Musl-libc project follow this pattern).

Static libraries are linked with object-file granularity and so if you have a static library libfoobar.a composed of*:

 foo.o
     foo1
     foo2
 bar.o
     bar

then if you link the lib for the bar function, the bar.o archive member will get linked but not the foo.o member. If you link for foo1, then the foo.o member will get linked, bringing in the possibly unnecessary foo2 function.

There are possibly other ways of preventing unneeded functions from being linked in (-ffunction-sections -fdata-sections and --gc-sections) but one function per file is probably most reliable.

There's also the middle ground of putting small number of related functions/data-objects in a file. That way the compiler can better optimize intersymbol references compared to -ffunction-sections/-fdata-sections and you still get at least some granularity for static libs.


  • I'm ignoring C++ name mangling here for the sake of simplicity
Oren answered 22/10, 2016 at 15:37 Comment(0)
P
2

I can see some advantages to your approach, but there are several disadvantages:

  1. Including a package is nightmare. You can end up with 10-20 includes to get the functions you need. For example, imagine if STDIO or StdLib was implemented this way.

  2. Browsing the code will be a bit of pain, since in general it is easier to scroll through a file than to switch files. Obviously too big of file is hard, but even there with modern IDEs it is pretty easy to collapse the file down to what you need and a lot of them have function short cut lists.

  3. Make file maintenance is a pain.

  4. I am a huge fan of small functions and refactoring. When you add overhead (making a new file, adding it to source control,...) it encourages people to write longer functions where instead of breaking one function into three parts, you just make one big one.

Pentameter answered 10/2, 2009 at 8:17 Comment(1)
Re: #4: Maybe you could limit yourself to one "public" function per file, and allow yourself to have as many static functions in that file as you need. That way, if I'm using your code and want to look up a function, I know exactly where to look. Also, you keep the concept of "everything in this file is related/for this single function."Sterigma
P
1

You can redeclare some of your functions as being static methods of one or more classes: this gives you an opportunity (and a good excuse) to group several of them into a single source file.

One good reason for having or not having several functions in one source files, if that source file are one-to-one with object files, and the linker links entire object files: if an executable might want one function but not another, then put them in separate source files (so that the linker can link one without the other).

Parfitt answered 10/2, 2009 at 5:34 Comment(2)
Better than static members of a class is putting the functions in a namespace. The syntax to use the functions is the same, but it makes the intention more clear.Middling
Most linkers will pull only functions that have been called in the final executable, so I don't think optimizing for the linker makes much sense.Middling
G
1

I also tried to split files in a function per file, but it had some drawbacks. Sometimes functions tend to get larger than they need to (you don't want to add a new .c file every time) unless you are diligent about refactoring your code (I am not).

Currently I put one to three functions in a .c file and group all the .c files for a functionality in a directory. For header files I have Funcionality.h and Subfunctionality.h so that I can include all the functions at once when needed or just a small utility function if the whole package is not needed.

Griseous answered 10/2, 2009 at 5:36 Comment(0)
L
1

An old programming professor of mine suggested breaking up modules every several hundred lines of code for maintainability. I don't develop in C++ anymore, but in C# I restrict myself to one class per file, and size of the file doesn't matter as long as there's nothing unrelated to my object. You can make use of #pragma regions to gracefully reduce editor space, not sure if the C++ compiler has them, but if it does then definitely make use of them.

If I were still programming in C++ I would group functions by usage using multiple functions per file. So I may have a file called 'Service.cpp' with a few functions that define that "service". Having one function per file will in turn cause regret to find its way back into your project somehow, someway.

Having several thousand lines of code per file isn't necessary some of the time though. Functions themselves should never be much more than a few hundred lines of code at most. Always remember that a function should only do one thing and be kept minimal. If a function does more than one thing, it should be refactored into helper methods.

It never hurts to have multiple source files that define a single entity either. Ie: 'ServiceConnection.cpp' 'ServiceSettings.cpp', and so on so forth.

Sometimes if I make a single object, and it owns other objects I will combine multiple classes into a single file. For example a button control that contains 'ButtonLink' objects, I might combine that into the Button class. Sometimes I don't, but that's a "preference of the moment" decision.

Do what works best for you. Experiment a little with different styles on smaller projects can help. Hope this helps you out a bit.

Lakh answered 10/2, 2009 at 5:48 Comment(0)
T
1

For the header part, you should combine items into logical groupings and create your header files based on that. This seems and is very logical IMHO.

For the source part, you should put each function implementation in a separate source file (static functions are exceptions in this case). This may not seem logical at first, but remember, a compiler knows about the functions, but a linker knows only about the .o and .obj files and its exported symbols. This may change the size of the output file considerably, and this is a very important issue for embedded systems.

Checkout glibc or Visual C++ CRT source tree...

Thereabouts answered 10/2, 2009 at 7:14 Comment(2)
Ha, I thought of it vice versa. I don't see a reason to put a lot of functions into a HEADER file because then you get compilation dependencies. But putting a lot of function implementations into a SOURCE would make sense to me ...Partheniaparthenocarpy
You do not have to take my word for it, just inspect visual c++ crt source code or the glibc...Thereabouts

© 2022 - 2024 — McMap. All rights reserved.