#What SHOULD and SHOULD NOT be put in CPP header files?

176 messages · Page 1 of 1 (latest)

regal oxide
#

I heard that header files aren't actually necessary to link CPP files in most cases (at least, for good compilers), but that there are some exceptions, such as templates.

Are there any other exceptions that are key to highlight? What about class definitions?

covert fieldBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

thin rover
#

header files are just copied and pasted into cpp files with #include so yeah, you technically don't need them

#

you could make a program with only cpp files, but it would be a mess to maintain

eternal yacht
#

Unless your public API handles the class by pointer, you must have its full definition before using it.

regal oxide
regal oxide
#

also what qualifies as "before"

#

can I get away with having the definition in a separate CPP file

#

or should I just put them in a H file at that point

#

although the issue with that is needing to have #include inside the H file

#

here's an example from some code i currently have:

// token.h
#pragma once

#include "files.h"

class PBT {
public:
    File file; // from files.h

    void RemoveFluff();
};
thin rover
regal oxide
#

not having to wait so long lol

thin rover
#

with just hours of work, you can save seconds of compilation time

regal oxide
#

i really do hate it

#

plus i hate getting errors like these

#

stuck on this atm

#

aaand fixed

#

anyway, i'd rather avoid headers if i can help it

#

but in that case, i could modify my question a bit

#

What SHOULD and SHOULD NOT be put in CPP header files?

#

i get not wanting to have excessive inline and whatnot

#

but are there not some things that can do fine without a header file

#

for example, if i have a function:

std::string LogError(std::string message);
std::string LogError(std::string message) {
    std::string buffer = "> ERROR: " + message + "\n";
    std::cerr << "\033[31m" << buffer << "\033[0m";
    return message;
}
#

is this fine?

#

is the first line even necessary? (i think it is, but honestly idk with C++)

gentle badger
#

if you start throwing in inline stuff then you can have inline definitions

gentle badger
#

first line is redundant with the second

regal oxide
#

not in a header

#

a CPP file

gentle badger
#

first line is a declaration of a LogError function
second line starts the definition of that function

the definition doesn't need to have a declaration as it can serve as it's own definition

regal oxide
#

so that i can get rid of the header

gentle badger
#

and why would you

regal oxide
#

compile time shrug

gentle badger
regal oxide
#

but to use it across files

gentle badger
#

like you'd have to have 100s of header indirectly including one another pulled over the network or something to start feeling the impact

#

you almost certainly have those files on your local disk

#

and based on what you're saying I doubt you have a ton of inline/template code

#

or that you're compiling your entire project multiple times per second

regal oxide
#

well, that's the thing
I want to avoid having headers unnecessarily including each other

gentle badger
regal oxide
#

which i'm already starting to get thanks to classes using types from other classes

#

or functions

gentle badger
#

you inherently have to include the class definition in order to make use of the class definition

#

and not doing that via headers is the best way to break things

regal oxide
#

is it not bad practice to have #include within headers though

gentle badger
#

because the moment an "upstream" class change, it must be changed consistently across the entire project

gentle badger
#

that's a guideline from C

regal oxide
#

oh.

gentle badger
#

it's better if you can limit the amount of includes within header imo

#

it's stupid to ban includes within headers

#

if your aim is to remove any and all includes from your headers you're quickly gonna run into unmanageable and unmaintainable stuff

#

if your aim is to limit the amount of includes to what is strictly necessary then sure

regal oxide
#

i'll send 2 files for example

gentle badger
#

you include what you use, period

regal oxide
#

buuuut

#

one sec

gentle badger
#

no but

regal oxide
gentle badger
#

you have a class as member in another class, you include the header defining the class

regal oxide
#

i assume i should not be including iostream in this header file

#

nor fstream, filesystem, etc.

gentle badger
#

the stuff you use only in the cpp you include only in the cpp

regal oxide
#

wait a sec

#

can i do...

#

this:

// header file
class File;

// cpp file
class File {
public:
    fs::path path;
    std::string extension;
    std::string name;
    std::ifstream raw;
    std::vector<char> data;
    std::ofstream file;

    int LoadFile(fs::path argv);
    void WriteVectorToFile(std::vector<char>& data);
};
gentle badger
#

the namesapce alias I'd rather you don't put at namespace scope or at least would do that in your own namespace, but if you're the end program then I guess

#

for std::ofstream you have it as member so cannot just kick out iostream

#

you need to get the definition for std::ofstream from somewhere

#

though I guess you could grab it from something smaller than iostream

regal oxide
#

need, but it still slows stuff down

regal oxide
#

would solve everything

#

otherwise uh

gentle badger
#

actually you have ofstream so arguably you'd kick out iostream and just include fstream

gentle badger
regal oxide
#

in the sense that it will work

#

and not error

gentle badger
#

if you ever hope to be able to use the class File in other file then absolutely not

regal oxide
gentle badger
#

the only file that can ever make use of File (as a complete type) is the source file

#

because that's where you put everything about it, aside from File naming a type

#

like class File; just says that File is a valid name/identifier and that it names a class, you can do nothing from that

#

except using it as a type name

#

and using it like an incomplete type

#

so you cannot call its member function

#

you cannot create any instance

#

you include what you use

#

that's it

regal oxide
#

hm

#

that's annoying then

#

that means any CPP that includes the header will also copy the other libraries

gentle badger
#

define "copy"

#

header includes are meant to include declarations first and foremost

#
  • classes
#

"copying" other libraries mean little in that context

#

if you have implementation details they in most case shouldn't be in your header, and if you want to break the chain of dependencies sometime you have to learn how to work around incomplete types

regal oxide
#

I was told that having headers include other libraries slows down compile time

gentle badger
#

but that has other cost

gentle badger
regal oxide
#

i do...

#

i mean, i've scrapped that project to start afresh

#

but i want to avoid that road

gentle badger
#

what kind of problem did you actually have

#

including too much is an issue but if you put everything as member you also don't have a choice in the matter

regal oxide
#

over 30 seconds to compile

#

and it only got worse from there

#

it adds up when you're needing to test things multiple times in an hour

#

especially as someone still learning and therefore needing to trial stuff for the sake of figuring things out

gentle badger
#

and how much inline/template code did you have

#

and were you using header only libs

regal oxide
#

iirc 11 files in total

gentle badger
#

because if you were, that's also kinda why you kinda don't

#

where you can

regal oxide
gentle badger
regal oxide
#

terminal

gentle badger
#

11 in parallel in 30s sounds weird

regal oxide
#

g++

gentle badger
# regal oxide terminal

like what, did you type a command to compile everything in one go with *.cpp or something?

regal oxide
#

nope, just named every .cpp

gentle badger
#

same thing

regal oxide
#

although i'm using *.cpp now because laziness

#

and yeah, it's the same thing

gentle badger
#

well that's one factor but assuming you had reasonably sized source files, and didn't include a ton of other header only libs that's abnormal unless you have a potato computer

regal oxide
#

my computer is fairly potato

#

but it's quick in other, smaller projects i've done

#

i think the header files are the source of my torment

gentle badger
#

it'd be less of a hassle if you had incremental builds, maybe

regal oxide
#

i've been told those are a real hassle to manage

#

and, although i don't understand the concept fully, it sounds like something that wouldn't work if you constantly go back and edit older files

raven dust
#

does someone know how to code a recursive program that switches every number 9 with a 7

regal oxide
covert fieldBOT
#

@regal oxide Has your question been resolved? If so, type !solved :)

regal oxide
#

i’d like a list or something to answer the post’s question…

gentle badger
#

for basic stuff you don't even need too much

gentle badger
#

one of the point of incremental builds is you only build/rebuild the part that were changed/impacted

#

instead of going through everything all the time

#

if you have the one god header that is included everywhere then yeah if you change something there you rebuild everything, but that's also kinda the point, you rebuild the impacted parts

#

also I'm not sure how g++ deals with multiple source files being passed at once, but usually you want to have the various source files dealt with in parallel

#

a proper config should provide you with that

#

now when you're working on a "component" you should only recompile a couple of files instead of the entire universe

#

as to whether your headers are structured in a way that favours short recompiles or not, that's way beyond the scope of what I've just described

#

having a good structure and having headers be as independent as possible is something that's hard to teach/explain

#

and it's arguably easier to discuss that with proper code to look at

#

I can throw generalities over things to do/avoid but considering you have to confront that to the actual project you're working on, it's hard to say anything meaningful that you can just blindly apply

regal oxide
#

time to figure out how VS works

#

i can’t even locate the damned Build button

gentle badger
#

if you meant visual studio you can navigate the gui or click the green arrow at the top to start the debugger

#

which will recompile/refresh if necessary, before starting up the program

#

if you meant vsc then that's a different story

regal oxide
#

weirdly

gentle badger
#

If you want to you can copy paste your files into the project directory, and use the GUI to "add existing files/items" instead of creating new files from scratch

#

I'm referring specifically to step 4

#

Or you learn cmake I guess but I guess part of the point was to bypass that step by relying on what vs provided

regal oxide
#

yeah

#

alright then, thank you Smugkeicho

#

hopefully VS’s automatic handling helps with my compile time issues

#

i’ll also just try to be more minimal about headers than i currently am

#

including like 20 libraries in a header is not a good idea…

#

!solved