Code Style When Interfacing With Libraries (C) - c

Recently I ran into a bit of an interesting problem in terms of coding style. Realizing that consistency is a key attribute of good code style, I inherited some code that had some interesting style patterns.
The code in question basically ties two libraries together. The program itself isn't too large. Code for utility functions that wrap the first library are found in a .h and .c file that total a whopping 100 lines (for both files). Code that interfaces with the second library is found in a .c file that also contains the main (total of 300 lines for this sole .c file). All functions that are written with the second library are made static since they are super custom to the particular implementation.
The problem I have is that each file has it's own style. The programmer in question has a style of his own, but the first set of files follows the style of the first library. The second file uses the style of second library. As a result, code in each file is locally consistent in terms of style, but the program itself spans several codes styles.
Style differences include using GLib types and functions like g_printf() in the second file and using C types and functions in the first set of files. In the second file (the one that interfaces with a library that uses GLib), there are portions that absolutely require use of GLib and others that do not. However, in order to maintain local consistency, the programmer used GLib throughout the file.
As a result, I'm wondering what the best practice is in this case in terms of code style. Here are the possible options as I see them.
Use the style from the first library in the first set of files and the style from the second library in the second set of files. This allows code from the library to match in terms of style and allows each set of files to be 100% consistent in terms of style locally, but not for the project as a whole.
Write the code with your own style, ignoring the style of the two libraries. GLib calls will be limited to where they are absolutely necessary. Standard C libraries will be utilized elsewhere. This will cause the code to locally not match styles between each file and their associated library calls. However, the code from file to file should appear somewhat consistent.
Pick one library's style to go with. While this should cause the project's code to be consistent, the code from this project to the programmer's other projec's will be inconsistent. Also, the source code file that has to follow the other library's style may look a bit off.
Looking forward to hearing any thoughts on this. I think this is the first time I've encountered a project's code shifting from one style to another with the same programmer. Thanks for your input and feedback.

In my opinion use the coding style of your company (If you are coding for company).It will help many other in future.
In case you have two different style maintain the coding style present there .
If it is necessary then change the style of coding in the library file.

I see two different problems:
Coding style
Dependency on glib
For what concerns the coding style, it should be uniform across the project. Choose the coding style and rules that you prefer (from the first file, from the second, or one of your choice). But make it consistent across the whole project. That will require some effort, of course, but it will pay you off in future.
Dependency on glib should be isolated as much as possible, in order to let you switch this library in future. In C++ you usually create a dependency on an abstract class, which is then inherited by a concrete class ("Program to an interface (i.e., an abstract class), not to an implementation (i.e., concrete class)". Since you are in C, and you don't have classes, try to mimic this behavior by decoupling the code.

Related

Cross-Platform C single header file and multiple implementations

I am working on an open source C driver for a cheap sensor that is used mostly for Arduino projects. The project is set up in such a way that it is possible to support multiple platforms outside the Arduino ecosystem, like the Raspberry Pi.
The project is set up with a platform.h file, with the intention of having different implementations of this header file. Like the example below:
platform.h
platform_arduino.c
platform_rpi.c
platform_windows.c
There is this (Cross-Platform C++ code and single header - multiple implementations) Stack Overflow post that goes fairly in depth in how to handle this for C++ but I feel like none of those examples really apply to this C implementation.
I have come up with some solutions like just adding the requirements for each platform at the top of the file.
#if SOME_REQUIREMENT
#include "platform.h"
int8_t t_open(void)
{
// Implementation here
}
#endif //SOME_REQUIREMENT
But this seems like a clunky solution.
It impacts readability of the code.1
It will probably make debugging conflicting requirements a nightmare.
1 Many editors (Like VS Code) try to gray out code which does not match requirements. While I want this most of the time, it is really annoying when working on cross-platform drivers. I could just disable it for the entirety of the project, but in other parts of the project it is useful. I understand that it could probably be solved using VS Code thing. However, I am asking for alternative methods of selecting the right file/code for the platform because I am interested in seeing what other strategies there are.
Part of the "problem" is that support for Arduino is the primary focus, which means it can't easily be solved with makefile magic. My question is, what are alternative ways of implementing a solution to this problem, that are still readable?
If it cannot be done without makefile magic, then that is an answer too.
For reference, here is a simplified example of the header file and implementation
platform.h
#ifndef __PLATFORM__
#define __PLATFORM__
int8_t t_open(void);
#endif //__PLATFORM__
platform_arduino.c
#include "platform.h"
int8_t t_open(void)
{
// Implementation here
}
this (Cross-Platform C++ code and single header - multiple implementations) Stack Overflow post that goes fairly in depth in how to handle this for C++ but I feel like none of those examples really apply to this C implementation.
I don't see why you say that. The first suggestions in the two highest-scoring answers are variations on the idea of using conditional macros, which not only is valid in C, but is a traditional approach. You yourself present an alternative along these lines.
Part of the "problem" is that support for Arduino is the primary focus, which means it can't easily be solved with makefile magic.
I take you to mean that the approach to platform adaptation has to be encoded somehow into the C source, as opposed to being handled via the build system. Frankly, this is an unusual constraint, except inasmuch as it can be addressed by use of the various system-identification macros provided by C compilers of interest.
Even if you don't want to rely specifically on makefiles, you should consider attributing some responsibility to the build system, which you can do even without knowing specifically what build system that is. For example, you can designate macro names, such as for_windows, etc that request builds for non-default platforms. You then leave it to the person building an instance of the driver to figure out how to configure their tools to provide the appropriate macro definition for their needs (which generally is not hard), based on your build documentation.
My question is, what are alternative ways of implementing a solution to this problem, that are still readable?
If the solution needs to be embodied entirely in the C source, then you have three main alternatives:
write code that just works correctly on all platforms, or
perform runtime detection and adaptation, or
use conditional compilation based on macros automatically defined by supported compilers.
If you're prepared to rely on macro definitions supplied by the user at build time, then the last becomes simply
use conditional compilation
Do not dismiss the first out of hand, but it can be a difficult path, and it might not be fully possible for your particular problem (and probably isn't if you're writing a driver or other code for a freestanding implementation).
Runtime adaptation could be viewed as a specific case of code that just works, but what I have in mind for this is a higher level of organization that performs runtime analysis of the host environment and chooses function variants and internal parameters suited to that, as opposed to those choices being made at compile time. This is a real thing that is occasionally done, but it may or may not be viable for your particular case.
On the other hand, conditional compilation is the traditional basis for platform adaptation in C, and the general form does not have the caveat of the other two that it might or might not work in your particular situation. The level of readability and maintainability you achieve this way is a function of the details of how you implement it.
I have come up with some solutions like just adding the requirements for each platform at the top of the file. [...] But this seems like a clunky solution.
If you must include a source file in your build but you don't want anything in it to actually contribute to the target then that's exactly what you must do. You complain that "It will probably make debugging conflicting requirements a nightmare", but to the extent that that's a genuine issue, I think it's not so much a question of syntax as of the whole different code for different platforms plan.
You also complain that the conditional compilation option might be a practical difficulty for you with your choice of development tools. It certainly seems to me that there ought to be good workarounds for that available from your tools and development workflow. But if you must have a workaround grounded only in the C language, then there is one (albeit a bad one): introduce a level of preprocessing indirection. That is, put the conditional compilation directives in a different source file, like so:
platform.c
#if defined(for_windows)
#include "platform_windows.c"
#else
#if defined(for_rpi)
#include "platform_rpi.c"
#else
#include "platform_arduino.c"
#endif
#endif
You then designate platform.c as a file to be built, but not (directly) any of the specific-platform files.
This solves your tool-presentation issue because when you are working on one of the platform-specific .c files, the editor is unlikely to be able to tell whether it would actually be included in a build or not.
Do note well that it is widely considered bad practice to #include files containing function implementations, or those not ending with an extension conventionally designating a header. I don't say otherwise about the above, but I would say that if the whole platform.c contains nothing else, then that's about the least bad variation that I can think of within the category.

Why is it mandatory to specify the module name at start of source file?

GHC insist that the module name has to equal the file name. But if they are the same, then why does a Haskell compiler need both? Seems redundant for me. Is this only a language design mistake?
Beside the inconvinience it also raises the problem that if I want to use 2 libraries that accidentially have the same top module name, then I can not disambiguate simply by renaming the folder of one of them. What is the idiomatic solution to this problem?
The Haskell language specification doesn't talk about files. It only talks about modules and their syntax. So there's clearly no language design mistake.
The GHC compiler (and many others) chose to follow a pattern of one module per file, and searching for modules in files with matching names. Seems like a decent strategy to me. Otherwise you'd need to provide the compiler with some mapping from module name to file name or an explicit list of every file in use.
I would say that one of the big reasons is that you don't always want the module name to be path to the file appended with the file name. This is the same as with Java, C#, and many other languages that prefer an explicit namespace declaration in the source code, explicit is better than implicit in many cases. It gives the programmer maximum control over their filenames without tying it to the filename only.
Imagine that I was a Japanese Haskell programmer, and my OS used Japanese characters for file names. I can write my source code using Japanese characters where possible, but I also want to export an API that uses ASCII characters. If module name and filename had to be identical, this would be impossible and would make it very difficult for people in other countries to use my library.
And as #chi has pointed out, if you have two packages with conflicting module names (a very rare occurrence in my experience), you can always use package-qualified imports.
The Haskell language specification requires that modules are started by a module header, and it does not mention files - it leaves total freedom for the implementing compilers regarding files. So the Haskell language lacks the ability to express where files containing modules are. Because of this some compilers [including the most important one: GHC] use a simple solution : the name of the module must match the path from an include directory to the file. This introduced the redundancy.
To avoid the redundancy, the compilers could drop the requirement in the language specification to start each module by a header. However they chose not to do this simply for the sake of confirming to the specification. Perhaps a GHC language extension could do this, but currently there is no such extension.
So the problem is a language design mistake, and lives on as legacy.
To combat possible name collisions among independent libraries, GHC extension Package-qualified imports seems the best way.

Any good reason to #include source (*.c *.cpp) files?

i've been working for some time with an opensource library ("fast artificial neural network"). I'm using it's source in my static library. When i compile it however, i get hundreds of linker warnings which are probably caused by the fact that the library includes it's *.c files in other *.c files (as i'm only including some headers i need and i did not touch the code of the lib itself).
My question: Is there a good reason why the developers of the library used this approach, which is strongly discouraged? (Or at least i've been told all my life that this is bad and from my own experience i believe it IS bad). Or is it just bad design and there is no gain in this approach?
I'm aware of this related question but it does not answer my question. I'm looking for reasons that might justify this.
A bonus question: Is there a way how to fix this without touching the library code too much? I have a lot of work of my own and don't want to create more ;)
As far as I see (grep '#include .*\.c'), they only do this in doublefann.c, fixedfann.c, and floatfann.c, and each time include the reason:
/* Easy way to allow for build of multiple binaries */
This exact use of the preprocessor for simple copy-pasting is indeed the only valid use of including implementation (*.c) files, and relatively rare. (If you want to include some code for another reason, just give it a different name, like *.h or *.inc.) An alternative is to specify configuration in macros given to the compiler (e.g. -DFANN_DOUBLE, -DFANN_FIXED, or -DFANN_FLOAT), but they didn't use this method. (Each approach has drawbacks, so I'm not saying they're necessarily wrong, I'd have to look at that project in depth to determine that.)
They provide makefiles and MSVS projects which should already not link doublefann.o (from doublefann.c) with either fann.o (from fann.c) or fixedfann.o (from fixedfann.c) and so on, and either their files are screwed up or something similar has gone wrong.
Did you try to create a project from scratch (or use your existing project) and add all the files to it? If you did, what is happening is each implementation file is being compiled independently and the resulting object files contain conflicting definitions. This is the standard way to deal with implementation files and many tools assume it. The only possible solution is to fix the project settings to not link these together. (Okay, you could drastically change their source too, but that's not really a solution.)
While you're at it, if you continue without using their project settings, you can likely skip compiling fann.c, et. al. and possibly just removing those from the project is enough – then they won't be compiled and linked. You'll want to choose exactly one of double-/fixed-/floatfann to use, otherwise you'll get the same link errors. (I haven't looked at their instructions, but would not be surprised to see this summary explained a bit more in-depth there.)
Including C/C++ code leads to all the code being stuck together in one translation unit. With a good compiler, this can lead to a massive speed boost (as stuff can be inlined and function calls optimized away).
If actual code is going to be included like this, though, it should have static in most of its declarations, or it will cause the warnings you're seeing.
If you ever declare a single global variable or function in that .c file, it cannot be included in two places which both compile to the same binary, or the two definitions will collide. If it is included in even one place, it cannot also be compiled on its own while still being linked into the same binary as its user.
If the file is only included in one place, why not just make it a discrete compilation unit (and use its globals via extern declarations)? Why bother having it included at all?
If your C files declare no global variables or functions, they are header files and should be named as such.
Therefore, by exhaustive search, I can say that the only time you would ever potentially want to include C files is if the same C code is used in building multiple different binaries. And even there, you're increasing your compile time for no real gain.
This is assuming that functions which should be inlined are marked inline and that you have a decent compiler and linker.
I don't know of a quick way to fix this.
I don't know that library, but as you describe it, it is either bad practice or your understanding of how to use it is not good enough.
A C project that wants to be included by others should always provide well structured .h files for others and then the compiled library for linking. If it wants to include function definitions in header files it should either mark them as static (old fashioned) or as inline (possible since C99).
I haven't looked at the code, but it's possible that the .c or .cpp files being included actually contain code that works in a header. For example, a template or an inline function. If that is the case, then the warnings would be spurious.
I'm doing this at the moment at home because I'm a relative newcomer to C++ on Linux and don't want to get bogged down in difficulties with the linker. But I wouldn't recommend it for proper work.
(I also once had to include a header.dat into a C++ program, because Rational Rose didn't allow headers to be part of the issued software and we needed that particular source file on the running system (for arcane reasons).)

What methods are there to modularize C code?

What methods, practices and conventions do you know of to modularize C code as a project grows in size?
Create header files which contain ONLY what is necessary to use a module. In the corresponding .c file(s), make anything not meant to be visible outside (e.g. helper functions) static. Use prefixes on the names of everything externally visible to help avoid namespace collisions. (If a module spans multiple files, things become harder., as you may need to expose internal things and not be able hide them with "static")
(If I were to try to improve C, one thing I would do is make "static" the default scoping of functions. If you wanted something visible outside, you'd have to mark it with "export" or "global" or something similar.)
OO techniques can be applied to C code, they just require more discipline.
Use opaque handles to operate on objects. One good example of how this is done is the stdio library -- everything is organised around the opaque FILE* handle. Many successful libraries are organised around this principle (e.g. zlib, apr)
Because all members of structs are implicitly public in C, you need a convention + programmer discipline to enforce the useful technique of information hiding. Pick a simple, automatically checkable convention such as "private members end with '_'".
Interfaces can be implemented using arrays of pointers to functions. Certainly this requires more work than in languages like C++ that provide in-language support, but it can nevertheless be done in C.
The High and Low-Level C article contains a lot of good tips. Especially, take a look at the "Classes and objects" section.
Standards and Style for Coding in ANSI C also contains good advice of which you can pick and choose.
Don't define variables in header files; instead, define the variable in the source file and add an extern statement (declaration) in the header. This will tie into #2 and #3.
Use an include guard on every header. This will save so many headaches.
Assuming you've done #1 and #2, include everything you need (but only what you need) for a certain file in that file. Don't depend on the order of how the compiler expands your include directives.
The approach that Pidgin (formerly Gaim) uses is they created a Plugin struct. Each plugin populates a struct with callbacks for initialization and teardown, along with a bunch of other descriptive information. Pretty much everything except the struct is declared as static, so only the Plugin struct is exposed for linking.
Then, to handle loose coupling of the plugin communicating with the rest of the app (since it'd be nice if it did something between setup and teardown), they have a signaling system. Plugins can register callbacks to be called when specific signals (not standard C signals, but a custom extensible kind [identified by string, rather than set codes]) are issued by any part of the app (including another plugin). They can also issue signals themselves.
This seems to work well in practice - different plugins can build upon each other, but the coupling is fairly loose - no direct invocation of functions, everything's through the signaling stystem.
A function should do one thing and do this one thing well.
Lots of little function used by bigger wrapper functions help to structure code from small, easy to understand (and test!) building blocks.
Create small modules with a couple of functions each. Only expose what you must, keep anything else static inside of the module. Link small modules together with their .h interface files.
Provide Getter and Setter functions for access to static file scope variables in your module. That way, the variables are only actually written to in one place. This helps also tracing access to these static variables using a breakpoint in the function and the call stack.
One important rule when designing modular code is: Don't try to optimize unless you have to. Lots of small functions usually yield cleaner, well structured code and the additional function call overhead might be worth it.
I always try to keep variables at their narrowest scope, also within functions. For example, indices of for loops usually can be kept at block scope and don't need to be exposed at the entire function level. C is not as flexible as C++ with the "define it where you use it" but it's workable.
Breaking the code up into libraries of related functions is one way of keeping things organized. To avoid name conflicts you can also use prefixes to allow you to reuse function names, though with good names I've never really found this to be much of a problem. For example, if you wanted to develop your own math routines but still use some from the standard math library, you could prefix yours with some string: xyz_sin(), xyz_cos().
Generally I prefer the one function (or set of closely related functions) per file and one header file per source file convention. Breaking files into directories, where each directory represents a separate library is also a good idea. You'd generally have a system of makefiles or build files that would allow you to build all or part of the entire system following the hierarchy representing the various libraries/programs.
There are directories and files, but no namespaces or encapsulation. You can compile each module to a separate obj file, and link them together (as libraries).

Did I read somewhere that C++/CLI moves us towards a "header-file" less society, like Java

And if so, should I be trying to keep header file use to a minimum when creating classes?
(This may come 3 years late, but it still shows up near the top on a google search, and the information is still valid today as it was 3 years ago).
First, yes you are correct, with C++/CLI you don't use .h files EXCEPT for native C++ classes, structures, etc.
The first question that may come to mind is how do I reference my class if I don't #include "someheader.h" file? There are two answers to this: 1) for classes OUTSIDE your project; 2) for classes WITHIN you project.
1) Add a reference to the .DLL (or to another project within the solution) in your .VCPROJ (Common Properties / Framework and References in the project property pages).
2) Add a #using otherclass.obj, thats right #using on an object file!
Within your project, assuming each class has its own source file, when you want to reference it in another class we make use of the #using "a_compiled_file" which can be a .dll or a .obj file.
In your Project Property Pages, under the C/C++ / General you will see: Resolve #using References, just add the MACRO string $(IntDir). This resolves to the intermediate directory for compiled source code.
The compiler reads the metadata within the .obj file (just like the metadata within a .dll file) and uses that for all the information it needs, no .h header file!
We do this because the C++/CLI compiler is TOO STUPID to remember our classes within the same project the way the C# compiler does.
NOTE: you may run in to sharing violations when using $(IntDir), in which case, pre-compile the files and put them in their own directory. The compiler nedds ONLY the meta data, so unless you are changing the class structure (think of .h stuff) it doesn't need to be compiled every time.
When calling between classes within a C++/CLI project, you have to #include. The compiler is encountering the cross-reference for the first time .
You should be #using ref classes between C++/CLI projects, rather than #include-ing. You're now beyond the compiler and managed referencing has taken over.
(Here's a good discussion on the topic: MSDN forums)
A pattern I have found useful in solutions that straddle the mixed managed/native world is to go "implementation-less" rather than headerless. This makes the most sense on new solutions that will have a mix of C++ and pure managed languages, it makes the C++ code "feel" more like the other managed code. Write .h files with
// MyClass.h header file
#pragma once
// full class implementation
And .cpp files with
#include "MyClass.h"
// nothing else
I think the .cpp is really optional and can be eliminated, though it is convenient to be able to Ctrl+F7 "build this file only." Whether or not it exists will affect build order.
I do NOT think it makes sense to re-organize existing C++ projects that have recently had the /clr switch added.
Even if C++ and C++/CLI were moving towards a header-less society, you are working now and you should work with the idioms in place for your code to be readable, maintainable. Way before modules become standard C++ and compilers implement them you will have to work with your own code and the code of others, there is no reason to strain your brain with different paradigms just because at some point you may want/need to learn them.
Just do what is idiomatic in your language of choice and keep an eye on how things advance, what changes are made, how code will be in the near future. I do not follow the advance of C++/CLI, but with plain C++, read about the next standard and try to learn the new libraries that are already in place (boost has some of them, compilers gcc/vs/comeau/intel/borland are already implementing C++0x functionalities to a different extent)

Resources