header file exposure - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: October 06, 2021, 09:35:38 am
content: I want to write a header file that will be used by only one C file and one other header file. Is there a way to limit the visibility of the file? Basically I don't want to write overly long defines to guarantee that they do not clash with something else, but if this file is included in the main header file with function prototypes I will expose it to the whole program.So I think something like:name.c - cotains the codename.h - contains the publicly available function typesname_internal.h - contains the private stuff and is only included in the c file.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: October 06, 2021, 09:50:48 am
content: It's traditional to put private stuff at the top of the .c file itself; or if you wish to avoid clutter, you can dump that into a "_private.h" or whatever, yes indeed.There's no mechanism to prevent another file including that header, this is all just convention and hackery in C to emulate what would be explicit OOP in a higher level language.Tim

author: Psi
author_rank: Super Contributor
timestamp: October 06, 2021, 10:18:42 am
content: Quote from: T3sl4co1l on October 06, 2021, 09:50:48 amI've not actually tried this but could you surround your private header with an #ifdef.Then, when you need to include it you do #define, #include, #undefine ?If that works it would protect that file in case it is ever included by accident somewhere else in the project.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: October 06, 2021, 10:50:15 am
content: I mean, whatever song and dance you put into it, can just be copy and pasted from another file into the target file. Preprocessor isn't quite Turing complete, nor stateful, so you can't implement just anything, like, locking it behind a crypto challenge-response or something. There's no semantic protection, it's simply the honor system whether something#includes it or not.Surrounding it with an#ifdefis a good idea anyway, but usually for purposes of including it just once -- this way if several headers include it in turn, there's no confusion.Tim

author: DiTBho
author_rank: Super Contributor
timestamp: October 06, 2021, 11:12:36 am
content: Quote from: Simon on October 06, 2021, 09:35:38 amrename internals as ".inc"name.c:Code:#include "name.h"...#define _name_internal_#include "name_internal.inc"name.h:Code:#ifndef _name_h_#define _name_h_...#endifthis can be used *only* if _name_internal_ is defined, and only name.c should define itname_internal.inc:Code:#ifdef _name_internal_...#endif

author: Simon
author_rank: Unknown
timestamp: October 06, 2021, 11:48:09 am
content: what i am thinking is if the contents of a header file is only used in the context of a file it is included into then by only including it in one c file it should only be visible to that one. A reference in a C file to anything in a header file that has not been #include'd in that C file throws an error.

author: magic
author_rank: Super Contributor
timestamp: October 06, 2021, 01:26:16 pm
content: Quote from: Simon on October 06, 2021, 09:35:38 amYes. If the main header needs some "internal" #defines then I'm afraid your only option is to #undef all of them at the end of the main header.If it's possible to extract all stuff dependent on internal #defines into a separate file then sure, do it. Files which don't include the "internal" .h file (directly or via the main / other header) will not see its #defines.

author: dferyance
author_rank: Regular Contributor
timestamp: October 06, 2021, 01:56:33 pm
content: Header files are to share common declarations and defines between c files. If you don't want it shared, don't put it in a header file. Instead, put it in the c file directly. Yeah you could do some kind of internal header but why? It is just splitting code between files without a design reason for it.

author: PlainName
author_rank: Super Contributor
timestamp: October 06, 2021, 06:04:01 pm
content: QuoteYou might want to do that for unit testing. For instance, in your .c you might have:Code:voidclkTickTimer_imp(xTimerHandle pxTimer){...}void (*clkTickTimer)(xTimerHandle pxTimer) = clkTickTimer_imp;and then in a .inc you would have:Code:extern void (*clkTickTimer)(xTimerHandle pxTimer);Both the .c and your unit test .cpp would then #include whatever.inc(Magically, your unit test code can now easily hook and redirect calls or other referrals to a function restricted to a specific .c file - the answer to 'design reason for it'. I'm sure there are many more.)

author: rstofer
author_rank: Super Contributor
timestamp: October 06, 2021, 06:05:17 pm
content: Quote from: magic on October 06, 2021, 01:26:16 pmAs pointed out above, put the private definitions in the .c file. If a header file doesn't exist, it can't be used nor the contents exposed unnecessarily.The .h file should expose only those constants, macros and functions that other functions actually need to know.Trying to get clever with something like <filename.inc> is not a solution as the file still exists and you have no real way to restrict its use.

author: PlainName
author_rank: Super Contributor
timestamp: October 06, 2021, 06:55:25 pm
content: QuoteFrankly, if a header is obviously private and someone nevertheless decides to include it elsewhere, they need sacking, not protecting. Otherwise you're best encrypting your source in case they stick a crafty #extern in their c file, eh.

author: Simon
author_rank: Unknown
timestamp: October 07, 2021, 07:33:04 am
content: what I am trying to do is replace register setup values with something meaningful that I can choose to use is a user setup headher that will then be put into the C code. So say I want to have defines for the internal ADC reference voltages. I define 0V55, 1V1, 1V5 etc in the private header, then these can be used in the setup header when I define V_ref, Basically it means I don't need to write one long file where the values the "user" sets up are right at the bottom because I cannot use something until it is defined. But I do not want to clash with any manufacturer definitions or definitions of other peripherals. So I have the internal defines and the setup defines as private to the ADC C file and the prototype functions go in the public ADC header file.The other way is to use defines or variables in the ADC C file that are using defines in a private header file saving one header file.

author: rstofer
author_rank: Super Contributor
timestamp: October 07, 2021, 04:02:48 pm
content: Quote from: dunkemhigh on October 06, 2021, 06:55:25 pmWhich is why file global variables, local to a file but shared among functions in the file should be declared 'static'. If this is done, they can not be reached by a 'extern' declaration but they can be shared by functions within the file.Functions within a file can also be declared 'static' in which case they can't be referenced from outside the file. Not only does this hide the function, it can help prevent name clashes.C is a pretty good language. It has all the protection features a programmer should need but they are pretty much worthless if they aren't used. Bottom line: Don't expose anything that you don't have to.

author: rstofer
author_rank: Super Contributor
timestamp: October 07, 2021, 04:31:06 pm
content: Quote from: Simon on October 07, 2021, 07:33:04 am'getters' and 'setters' - functions that 'get' a value for a user in user units and 'set' a range (or whatever) again, in user units. These are really common in C++ but they are also a nice way to provide the user interface for C programs. The user has no need to know how or even where an analog value comes from. They don't need to know that some 5th order polynomial was used to calculate the value, all they need is the value in user units. Obviously, the header file will declare the getter and setter functions but that's about all. The fact that you just changed ADCs doesn't impact the user at all. User units are user units... Yes, the getter and setter functions will need to change but that should be local to a single .c file. If the .h file is properly written, it won't even change when the device changes.There is no need for a header file that impacts only 1 .c file. Header files should be used to share information and if it shouldn't be shared, it doesn't belong in a header. There's simply no reason to avoid putting #defines inside .c files. That's about as close to the point of use as you can get and makes editing a lot easier. All the header file should do is declare the functions for external use and, by including in the related .c file, provide a forward declaration of the functions which makes certain that the .c and .h declarations are identical.Never tell the user anything they don't absolutely need to know!

author: PlainName
author_rank: Super Contributor
timestamp: October 07, 2021, 06:36:02 pm
content: QuoteWhat he said.Although I suspect the issue will then just move to how the getter/setter implements things. Somewhere along the line there has to be an actual conversion, and it's either a bit of calculation or a ton of defines. Which brings us back to where we were

author: Simon
author_rank: Unknown
timestamp: October 07, 2021, 06:46:07 pm
content: Yes I can define the setup values in the c file, that just eliminates one of the header files, there will still be the file that the world sees and the file that only the c file sees.

author: ejeffrey
author_rank: Super Contributor
timestamp: October 07, 2021, 08:16:18 pm
content: Quote from: Simon on October 06, 2021, 09:35:38 amJust do exactly that then #include both headers from name.c. You should include name.h first and name_internal.h last. Nothing you can do can stop someone from importing the definitions of name_internal.h if they really want to, nor can you stop an idiot who is just typing random things in hopes that it will work. All you can or should try to do is make it less likely for a knowledgeable and well meaning person to do so unintentionally. Being clear with the name and not trying to do anything unnecessarily clever is the way to accomplish this.If you really want to isolate things compile to name.o and distribute that along with only name.h.Or just put those private details in the C file. The purpose of header files is to enable including from multiple locations. A header file that can only be used from one file is unnecessary. However, sometimes you may have multiple cooperating C files that need to share the private declarations, then using _private.h makes perfect sense.

author: blacksheeplogic
author_rank: Frequent Contributor
timestamp: October 07, 2021, 09:07:36 pm
content: Quote from: Simon on October 07, 2021, 06:46:07 pmHave you thought about using -DBUILD_ADC_VALUE=12 for example. You can use this as a base definition and make use of it by the preprocessor.

author: Simon
author_rank: Unknown
timestamp: October 08, 2021, 08:42:38 am
content: Well this is me trying to make things for future me. There are no other people involved. Several minutes later I am as clueless about what I just wrote as the next person.

author: Siwastaja
author_rank: Super Contributor
timestamp: October 08, 2021, 12:16:40 pm
content: -D on command line for a few parameters, or those that I need to build different versions with.For example, if I have two PCB revisions on the field (and those can't be disposed of), then it's -DPCBREV=1B.Or if you have two versions with different battery voltage and the firmware needs to know what it is to correctly implement battery indication, and you don't want to do it runtime configurable parameter, then -D is your friend, you always know what you are compiling and can quickly do different versions.For large number of mostly rarely changing constants and #defines, some config.h that can be included from multiple files. Everything must be static const, or #defines, so they work over multiple files. I prefer one such config.h even if these are used in multiple places. Everything here should be truly something that can and will be modified; for stuff where change of constant requires code review or code changes, just define them in the related module (thing.c), close to where they are used. If module becomes too long for you to "find" the constant definitions, don't try to mitigate by moving them to a "common place", instead split the module functionality.These are all simplifications for small projects on small teams, larger solutions involve configuration scripts (think about configure-make-install pattern).

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: October 08, 2021, 04:01:09 pm
content: Quote from: Simon on October 06, 2021, 09:35:38 amThere's no way to prevent a maliciously curious user from `#include`ing your private header file, if they set their mind on doing so.So, all you can do in this case is give your internal header an "internal sounding" name and/or bury it deeper in a directory structure, i.e. place it into a dedicated directory for "internal" headers.Of course, you can add an extra layer of protection, like require an extra `#define I_AM_THE_LIZARD_QUEEN` from anyone trying to include your header, just to prevent accidental inclusion. But normally it is not necessary.

author: PlainName
author_rank: Super Contributor
timestamp: October 08, 2021, 05:08:19 pm
content: QuoteActually...Just shove a global in there:Code:int whoopsie;The compiler/linker will complain if the header is included more than once.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 08, 2021, 05:52:51 pm
content: Quote from: dunkemhigh on October 08, 2021, 05:08:19 pmUnfortunately, it may not make the compiler nor the linker complain.QuoteAFAIR, GCC for instance used to allow multiple definitions of a global variable in older versions. It stopped allowing this by default with relatively "recent" versions.You can still make it allow multiple definitions using the attribute '__common__'. Such as:Code:__attribute__((__common__)) int whoopsie;The '-fcommon' command-line flag should also make GCC allow multiple definitions.But try with various versions of GCC or other compilers and report back. Problem is, according to the standard, allowing multiple external definitions is a "common extension", which means that your solution is not guaranteed to have the effect you want here.Even multiple definitions in the same file are allowed - those are called "tentative definitions", and are actually not an extension, but standard. Those must follow certain rules though, such as not having an initializer.QuoteYeah. C is fun.

author: PlainName
author_rank: Super Contributor
timestamp: October 08, 2021, 06:36:20 pm
content: QuoteBum. Could've been a neat dirty hackQuoteCode:int whoopsie = 1;

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 08, 2021, 07:30:08 pm
content: Quote from: dunkemhigh on October 08, 2021, 06:36:20 pmAs you can read, in this case, the behavior is *undefined*. And for compilers allowing multiple external definitions, they usually don't care one bit if there are initializers or even if the types are different!You can check this with GCC, either old versions, or passing the '-fcommon' option flag. You'll see for yourself.

author: PlainName
author_rank: Super Contributor
timestamp: October 08, 2021, 08:17:17 pm
content: QuoteThat's what made me think of it - I got caught with exactly this issue the other day when including a private header in a unit test module.

author: DiTBho
author_rank: Super Contributor
timestamp: October 09, 2021, 12:59:15 am
content: it also goes wrong with Linux. When people redefine a critical constant and things start breaking but not immediately because for some weird reasons there is still the old definition around and it's the right order. But then someone removes the file and the kernel compiles but it doesn't even show a line on the console ...... that is precisely when C is "funny"I wasted two weeks on such a stupid thing, because I had no idea that was the problem, so I checked other things.

author: Simon
author_rank: Unknown
timestamp: October 09, 2021, 07:25:56 am
content: I'm not making it super idiot proof, it only for future idiot me. If someone wants to include my file in the future I would at least expect them to read it and the first line will say that it is private to the C file it belongs to.

author: PlainName
author_rank: Super Contributor
timestamp: October 09, 2021, 09:57:30 am
content: QuoteWell, quite. This kind of thing should be aimed at preventing accidental or unwitting screwups. There has to be an assumption that the user is at least semi-competent, and if he isn't then that's his problem. We ain't the police or bodyguards.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2021, 05:48:19 pm
content: There could have been a way using the preprocessor - at least with GCC.GCC has a number of non-standard predefined macros. The one which could help here is '__BASE_FILE__'. Within the scope of an included file, it holds the name of the base file - the file that includes it.It's unfortunate though that you can't use string literals in preprocessor comparisons.Something like this, placed in the header file in question, assuming the .c file in which it's only allowed to be included is name 'allowed.c', would have been a candidate:Code:#if __BASE_FILE__ != "allowed.c"#error Included in the wrong file!#endifBut the preprocessor can't use string literals in this context, so it just gives an error (token ""allowed.c"" is not valid in preprocessor expressions). That's too bad.Now if you can find a clever way of using this '__BASE_FILE__' macro to achieve the same result, there you go...

author: PlainName
author_rank: Super Contributor
timestamp: October 09, 2021, 06:12:35 pm
content: QuoteI think using a compiler-specific feature may cause more problems than it solves. Whatever solution is used (even if that's "DNF") it needs to work with any standard-compliant compiler, and hopefully most that are more-or-less C.

author: SiliconWizard
author_rank: Super Contributor
timestamp: October 09, 2021, 07:21:09 pm
content: Now a simple approach not requiring anything non-standard would just be the following:Add this in the header file 'xxxx.h':Code:#ifndef ALLOWED_FILE#error Included in the wrong file!#endifTo include it in another file:Code:#define ALLOWED_FILE#include "xxxx.h"Failing to define the 'ALLOWED_FILE' macro (name it as appropriate) before including the header file will yield an error.So to include it, you have to explicitely define this macro. That should be enough to prevent unintended including, and would further self-document where it is appropriate to include it.

author: Simon
author_rank: Unknown
timestamp: October 09, 2021, 08:26:27 pm
content: I don't really need to guard from it being included by mistake.

author: PlainName
author_rank: Super Contributor
timestamp: October 10, 2021, 10:52:32 am
content: QuoteI reckon that passes the test

author: gnif
author_rank: Unknown
timestamp: October 12, 2021, 04:03:55 am
content: Might have already been mentioned here but what I do is take advantage of the include directory flags for the pre-processor. IeCode:project- module1- module1.c- internal1.h- module2- module2.c- internal2.h- include- public.hAssuming gcc... `gcc -c -Iinclude module1/module1.c -o module1/module1.o`Because the private internal header of module2 is not in the include path, unless someone uses a nasty `../` in their include path they can't directly access the private header of another module.

author: Jan Audio
author_rank: Frequent Contributor
timestamp: October 18, 2021, 01:43:21 pm
content: Very funny.You want not use long names, instead you want short defines, and you are worryd that they overrule some other define.1 : Are you really typing ?, i use ctrl C + V mostly.2 : You want two of the same defines that each file have a different function for the define, confusing.3 : Programming most time-consuming and most-difficult task is making names for your variables.You can instantly spot amateur code by the defines or no defines at all.My advantage is i can use two languages for defines and variables.