15

I can use using namespace directive to avoid identifier/variable name collision, but what happens when file names or library names collision happens in large projects.

In C the conventional approach is to add files recursively using #include_next directive. How can I achieve the same in C++ without using the #include_next directive and address the issue of duplicate file names among applications and shared libraries. An example, a work around the class() function in AIX math.h clashing with identifiers named "class".

/* GNU Lesser GPLv2.1 or later */
#ifndef FFMPEG_COMPAT_AIX_MATH_H
#define FFMPEG_COMPAT_AIX_MATH_H

#define class some_text

#include_next <math.h>

#undef class

#endif /* FFMPEG_COMPAT_AIX_MATH_H */

EDIT: Can I use, for example class machine-instruction-set where the binary has to run on multiple platforms? Can there be namespace collision in such case?

manav m-n
  • 11,136
  • 23
  • 74
  • 97
  • 38
    What on earth is `#include_next` ? – Alexandre C. Sep 28 '13 at 13:14
  • 1
    @AlexandreC. A [GNU extension](http://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html). Surely not a "conventional approach", I'd say. – jrok Sep 28 '13 at 13:18
  • Hrm, I thought it appeared in Clang first, but might be wrong. – Per Johansson Sep 28 '13 at 13:18
  • 15
    Conventional? *The use of `#include_next` can lead to great confusion. We recommend it be used only when there is no other alternative. In particular, it should not be used in the headers belonging to a specific program; it should be used only to make global corrections along the lines of `fixincludes`.* ― the GCC manual. – n. m. could be an AI Sep 28 '13 at 13:24
  • @n.m. Yes, Conventional, I can't find any other examples not using `#include_next`. Moreover, `#include_next` should be used only by experts. – manav m-n Sep 30 '13 at 13:36
  • @Manav I feel even experts should avoid shady stuff like `#include_next` because code they write may be maintained by non-experts too. – HAL Sep 30 '13 at 13:57
  • Furthermore, `#include_next` has nothing to do with namespace collisions. It's a mechanism for wrapping known broken headers. – n. m. could be an AI Sep 30 '13 at 15:06
  • @n.m. Perhaps, I may not be a big fan of C++, but sometimes, the `old-fashioned` ways are the best for the `jobs` to be done. – manav m-n Oct 02 '13 at 06:20
  • I'm not sure what kind of job you are trying to get done. Perhaps you can show us an example. – n. m. could be an AI Oct 02 '13 at 06:35
  • @n.m. Example Added <@edit> – manav m-n Oct 02 '13 at 07:22
  • 1
    `using namespace` frequently will give you many problems. Try not to use it. Instead you can use for e.g.`using std::vector`. Don't include whole namespaces. – zoska Oct 02 '13 at 09:11

6 Answers6

29

I can use using namespace directive to avoid identifier/variable name collision

Quite to the contrary, using namespace directive introduces collisions. You resolve collisions by specifying scopes, e.g. std::vector<> vs. boost::numeric::ublas::vector<>.

... but what happens when file names or library names collision happens in large projects?

Filename collisions are easy to prevent by being systematic: organize your headers so that they mimic your namespaces, e.g. boost::numeric::ublas::vector<> comes from #include <boost/numeric/ublas/vector.hpp>. And don't pile headers and sources of different libraries in one directory, so that you can include headers with the same name using a different directory prefix, e.g. #include <lzma/version.h> vs. #include <linux/version.h>.

Community
  • 1
  • 1
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
8

The correct way to deal with this is to setup your build environment in a way that name clashes become less likely.

Guidelines for writing libraries

For example, most third party libraries do not introduce plain files to the compiler's include path. Instead they introduce a directory that contains the files. You can increase flexibility by introducing subdirectories for different modules.

Consider the directory structure of Boost. On the top-level, Boost only introduces a single name to the include search path: The boost directory. That way, even though boost introduces a number of header filenames which are likely to clash (like array.hpp, thread.hpp, or function.hpp), they are all wrapped away in the subdirectory:

#include <boost/thread.hpp> // this is boost's header
#include "thread.hpp"       // some header specific to my current project
                            // no name clash :)

The same concept is used for the different libraries that ship with Boost. For example, both Boost lockfree and Boost assign have a queue.hpp header. But they live in different subdirectories, so there is no clash:

#include <boost/lockfree/queue.hpp>
#include <boost/assign/std/queue.hpp>   // no clash :)

To make finding the right header file easy, Boost uses the same structure for include files and namespaces: The lockfree queue lives in boost::lockfree namespace, while the functions from the assign queue header go to boost::assign. That way it is not only straightforward to find the matching namespace from an include file and vice versa, it also decreases the likelihood of namespace clashes, as a namespace clash is also likely to manifest in a physical name clash on the file layer.

You can adapt these guidelines for your own project

  • Don't introduce bare include files directly to the include path. Instead group them together in a directory to avoid polluting the include path with names.
  • Use directory trees to further structure the include files of modules inside a single library.
  • Try to match the physical structure with the logical structure. Filesystem include paths should resemble namespace hierarchies if possible.

This avoids most of the nameclashes in the first place. The question is what if you have to use a third-party libraries that do not follow these rules and you get a clash that is outside your control?

Guidelines for dealing with name clashes by third-party libraries

The answer is to be brutal and enforce the separation through the build environment. Reorganize the include paths by moving conflicting libraries into uniquely identifiable subdirectories to resolve physical conflicts. This is usually non-critical. Logical conflicts require patching and recompiling, which is a lot more inconvenient. But if you really run into name clashes here, it is a sure indication that at least one of the library vendors did not do their job too well and you should consider filing a bug.

Stay away from ad-hoc fixes like #include_next to fix physical clashes or preprocessor defines to fix logical clashes. They are dirty hacks and while they might solve your problem temporarily, they are very likely to come back and bite you eventually.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
5

File Name Collisions

Put libraries in separate subdirectories, and set the parent directory as the search location. So instead of:

#include "zerz.h"  // supposed to be from libfoo
#include "zerz.h"  // supposed to be from libbar

You can do this:

#include "libfoo/zerz.h"
#include "libbar/zerz.h"

Identifier Collisions

Use the pimpl idiom to isolate the code that interacts with each library so that the conflicting identifiers aren't dragged into zillions of projects due to transitive includes.

For example, if libfoo and libbar both have a function called Frobnicate, then you want to isolate any code that depends on those libraries so that nothing else has to include those libraries' headers and thus end up with a conflict. Only the .cpp (or .c) file that actually calls Frobnicate should #include the library's header file. That prevents unintended transitive includes, which is usually how you would end up with conflicting declarations of Frobnicate being included in a single compilation unit.

The pimpl idiom is usually presented terms of C++, but you can play the same game in C. The point is to define your own API to the library in a header. Your API should have the same functionality but using names that don't conflict (e.g., by adding a unique prefix if you can't use namespaces). All code should be able to include that header without conflicts. You then provide an implementation file (.cpp or .c), which is the only file that #includes the actual library header. This implementation file essentially forwards calls of the prefixed functions to the actual library functions. All you have to do is avoid collisions in this one file, which should be doable.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 2
    If _libfoo and libbar both have a function called Frobnicate_ and that function has C linkage you get a linker symbol collision in the best case. If `Frobnicate` comes from different `.so` - you get UB / runtime errors. – Maxim Egorushkin Sep 30 '13 at 14:14
  • @Maxim Yegorushkin: Good point. I'm not sure how you deal with linker collisions in the Linux world. In Windows, you can use a .def file to rename a problematic symbol when you compile the library, which should then avoid the problem when doing the final application link. – Adrian McCarthy Sep 30 '13 at 17:20
  • BTW: I was assuming that the problematic libraries were third-party code that the poster is trying to incorporate into the application. If you own one or both of the conflicting libraries, the answer is to rename the problematic identifiers. – Adrian McCarthy Sep 30 '13 at 17:23
  • 2
    In Linux world one sometimes can work around linker symbol collisions using [partial linking](http://www.math.utah.edu/docs/info/ld_2.html#IDX69), by linking object files that require the conflicting symbols to the corresponding object files that define the symbols, so that conflicting symbols get resolved early without a conflict (i.e. the defining object files get never mentioned together in a linker command line). Not sure how widespread this method is, I only had to employ it once but haven't seen much mentioning of it aside from the short description in `man ld`. – Maxim Egorushkin Sep 30 '13 at 20:51
3

First of all, #include_next is not standard, is a gcc extension, also supported by Clang, but I don't know if any other compilers support it.

The only way to deal with filename collisions is to properly use either #include "foo.h" or #include <foo.h>. The former is meant to be local to the current project, while the latter is used for system libraries. Usually #include "foo.h" will also search system libraries, but not the other way around.

Anyway, #include "foo.h" should start by looking in the source file directory, you can use #include "../foo.h" etc. to do relative includes. If you have filename collisions within the same project, you're going to have to use different compile flags to set different search paths (basically make subprojects).

For symbol conflicts, #define before #include is the standard way.

Of course, the best way is to take care to avoid these kind of problems in the first place.

Per Johansson
  • 6,697
  • 27
  • 34
1

A useful method to use when using C includes in C++ programs is to use the following code snippet in your .h files.

#ifdef __cplusplus
extern "C" {
#endif

<your code here...>

#ifdef __cplusplus
}
#endif

This will allow your C++ objects to link with the C objects. I don't know about going the other way though, so perhaps this is a partial answer.

DAhrens
  • 380
  • 1
  • 10
  • And a short bit after I posted that, this came to my attention: http://stackoverflow.com/questions/1041866/in-c-source-what-is-the-effect-of-extern-c – DAhrens Oct 04 '13 at 19:41
0

"I can use using namespace directive to avoid identifier/variable name collision": Nope! You should avoid using that directive in order to avoid name collisions.

Nabin
  • 11,216
  • 8
  • 63
  • 98
Alan
  • 1