chevron_left chevron_right
Login Register invert_colors photo_library

Tutorial Managing namespaces in C [simple] filter_list
Managing namespaces in C [simple] #1
Many people flock to C++, Java, etc because the languages support namespaces and help to prevent naming conflict between functions. In these languages, code is placed inside of a "namespace" (or in the global namespace if none is specified), which is just a fancy term describing what can be thought of as a packing box or folder on your filesystem. In order to use these functions, you need to specify the namespace they're in first. There are genuine applications for these practices, but the age old argument of "keeping the global namespace from becoming cluttered" is a foolish one. For my first tutorial this year, I'll go over best practices and useful tactics when it comes to keeping your namespace clean when working with C.

1. What is the global namespace?

In C there are a few levels of scoping. We're only going to cover 3 here (because that's all that ANSI C has, and I'm a luddite): global, file-local, and local. Many of you will recognize those terms almost instantly as you probably have some experience with both of them when it comes to declaring and using variables. A few of you may want to pipe in and tell me that global variables should always be avoided (you're partly wrong), but there is one type of declaration that always happens in the file-local space, and very often erroneously in the global namespace. These are functions. What this means, is that whatever functions you make are "global" to anything in that file that comes after the prototype (or the declaration itself, whichever comes first). If you're only working with one file, then this space is the "global" namespace. You can only have a single function, variable, or prototype with the same name in this namespace. This means that if you want to write your own version of printf, you can't have stdio.h included in that file, the namespace already has one of those and you will get a linker error.

2. How do functions enter the global namespace

As I discussed in the previous point, if you only have a single file to work with, then anything declared in the root level of that file is already in the global namespace. This is not most projects. Often C projects contain many hundreds of files, sometimes even additional libraries that are compiled from source and then linked in. Let's take up the hypothetical case that we're building a library that other people will use. We will have a couple files, usually a makefile, some .c files, and some .h files. When the C compiler runs over our source code, it will generate a symbol for each function, variable, etc that exists at the root level of each file, declare it in the object table (varies by OS), and place the relevant machine code into that file. These symbols are usually named identically to the name of the function, often prefixed with an underscore. Their offset in the table is where the machine code for that function begins. When we create the final library, however (that is, when we link together all of the object files into a .a [static library], .so [linux dynamic], .dylib [macos dynamic], .dll [windows dynamic], etc) those symbols are lost. Here is an example using code that was in my /tmp
[Image: KJDTitl.png]
here we can see the sets of symbols in both files. Let's go ahead and turn that into a static library.
[Image: 2dlBtUB.png]
As you can see, abs8 is still in the file. This is not typical, but it can be confusing. Is abs8 in the global namesapce? Let's find out by making a driver that also includes abs8 and linking our static library to it.
[Image: zUs8V7G.png]
Now that's weird. we got -1 as the return code (bash does not allow negative return codes, -1=255). You know what else is weird? I don't see test and test1 in that symbols list. Why is that? because abs8, test, and test1 are not in the global namespace. They are file-local to test.c and test1.c respectively. In order to promote them to the global namespace, we'll need a header file. These files are incredibly useful for libraries because they give the library user a convenient way to have access to a specific set or subset of the library's contents.
[Image: 7bicTID.png]
You will notice that even though I did not include abs8 in the header file, it still made it to the global namespace. Why is this? Because the symbol exists in our library file. This clutters the global namespace. abs8 was intended to be a function that the library uses internally, the programmer doesn't need it, and may want to name something the same way. To solve this issue, we need to change how abs8 is defined. We're going to modify it and make it static, and then strip the symbol out of our object file.
[Image: osOun3a.png]
Now, let's go ahead and try to compile our library and program (that includes abs8) again.
[Image: IGwytdI.png]
And, let's have another look at the symbols for the entire file.
[Image: MOKaKCY.png]
Here, you can see that we were able to make our own abs8, despite test.a having its own that's named the same thing. We've avoided namespace pollution in this case.

3. How to manage your namespace
Let's modify our library a little bit so that it more closely resembles something that you might see.
[Image: JMjPBJ9.png]
Ok, now let's have a look at what's going on here. I've written up a simple test suite that tests out some mathematical operations on our system. First up is math.h and math.c. These are my internal mathematical operations, I don't want to expose these in my library. I'm putting them all in one file not to hide them, but because they can be logically grouped this way.
[Image: 4v9HNrP.png]

Now let's take a look at the two test suites:
[Image: oKq7TOH.png]

As you can see, I separated the two suites by the degree of freedom they allow the programmer. This is an extremely basic example, so I had to find some reason to separate them. Now, I have two header files, floating and integer. For this library I decided to allow the user to run specific sets of automated tests based on the type of operation being tested. The files look like this
[Image: uYsxG8C.png]

This way, if the programmer wants to make sure that their floating point unit is operating correctly (assuming these tests were more complex), they don't need to bring in all of the symbols in the library, but only the subset they need. That's exactly what I did in the driver file.
[Image: uNlBUzf.png]
In this case, we'll have one extra symbol in our namespace (TestFloat2) instead of 3 extras.

The last file, of course is the Makefile. This just automates tasks. I'll provide it in a spoiler for reference.
[Image: ET4n5sP.png]

Let's give it a run and then look at the symbols.
[Image: V1wWxV1.png]
Perfect! not only did it drop out TestFloat2 (but not from code-level), but we see it ran perfectly!
The lesson to be learned here is to avoid those header files like boost.h, unistd.h, etc that just include a whole bunch of other header files. only include the files that contain the prototypes you need.

4. What if I only need one function?
This one happens a lot more often than you think. Somebody is making a library or program, and in one case they need to use printf, so they include the entirety of stdio. This is wrong. stdio is a massive API. If you're making use of a good portion of it (I'd say more than 3 functions, or any defined constants) you should include it. Apart from that, including the whole API reference for just a single or pair of functions only clutters up your available namespace. There are two ways to go about not doing this. Here are examples:
[Image: u01WOq0.png]
In this example, we only care to instruct the compiler that printf exists and returns an integer. The return type is only needed if it's not int, but you should always include it anyways. The compiler will often warn you for doing this, as it will be forced to disable parameter list validation (yelling at you for not including enough, too many, or the wrong type of arguments). What is advised is to look at the reference manual for the function you are going to use, and then paste in the decl line instead.
[Image: xRS2Dgm.png]
[Image: mKyrA8q.png]

At this stage, I'm getting tired of writing this tutorial, but that doesn't mean I'm finished. I'd be happy to write follow up tutorials about how to take namespacing to the extreme, and how to manage absolutely massive projects with ease.


RE: Managing namespaces in C [simple] #2
Unrelated to the content of the tutorial, but more about how to write them. What turned me off from continuing past the intro was the blah screenshots, even though I'm sure they're useful.
I'll come back to this thread when I start C for school.

[+] 1 user Likes sarcrates's post

RE: Managing namespaces in C [simple] #3
(02-12-2019, 05:26 AM)sarcrates Wrote: Unrelated to the content of the tutorial, but more about how to write them. What turned me off from continuing past the intro was the blah screenshots, even though I'm sure they're useful.
I'll come back to this thread when I start C for school.
@sarcrates here are some tutorials on C programming.
My IT skills that I know perfect is SQL, HTML ,css ,wordpress, PHP.
coding skills that I know is Java, JavaScript and C#


RE: Managing namespaces in C [simple] #4
(02-12-2019, 12:49 PM)darkninja1980 Wrote:
(02-12-2019, 05:26 AM)sarcrates Wrote: Unrelated to the content of the tutorial, but more about how to write them. What turned me off from continuing past the intro was the blah screenshots, even though I'm sure they're useful.
I'll come back to this thread when I start C for school.
@sarcrates here are some tutorials on C programming.

Not to rain on your parade here, but those aren't useful in the slightest. C tutorials written by C++ programmers ignore 90% of the most useful features that C has. For the same reasons that you shouldn't be writing software in python, you shouldn't be writing high level user software in C. That's just not it's place.


RE: Managing namespaces in C [simple] #5
Thanks for the tut


Users browsing this thread: 1 Guest(s)