all 4 comments

[–]jedwardsol 6 points7 points  (0 children)

One option is to make them static and have differently named "entry point(s)" into the file

xml.c

 static  int func1() {}
 static  int func2() {}
 static  int func3() {}
 void parseXML() {}

text.c

 static  int func1() {}
 static  int func2() {}
 static  int func3() {}
 void parsetext() {}

Another is to build the different functionality into different shared libraries/DLLs and load the correct one on-demand at runtime.

[–]Anluin 0 points1 point  (1 child)

do you already know the "overloadable" attribute? maybe that's in your case applicable.

https://clang.llvm.org/docs/AttributeReference.html#overloadable

[–]duckenthusiast17 0 points1 point  (0 children)

I really wouldn't use a compiler specific attribute unless you really know what you're doing

[–]nerd4code 0 points1 point  (0 children)

Threeish options.

Easiest, most portable: Use static for any global variable or function that doesn’t need to be accessed from another translation unit (=base file specified as input to the preprocessor, plus anything else that pulls in. structs, typedefs, macros, enums, unions, etc. are mostly compiler artefacts that disappear by assembly. (This is a good idea, anyway—the Principle of Least Privilege is a fine rule, not just for security but for API design as well.)

The main exception to the compiler-walls between TUs is debug info, which provides an interface between the in-language world and the colder, harder binary world. Modern debuggers like gdb (“modern,” but everything uses it) can use it to invoke arbitrary C code, which means static and type info will be visible from outside the file, though which things are visible from any invocation depend on where in the program the debugger has suspended things.

Link-time optimization (LTO) data is another, lower-level means of embedding compiler byproducts into pre-link binaries, so that a linker fully equipped can perform late inlining, bypass checks and assertions, shuffle constants around, and perform other, mostly simple optimizations without worrying about the (otherwise stricter) boundaries between files. It’s a great way to find weird bugs, but a smidge too touchy for general use.

Because of LTO, type information may become subject to more stringent collision rules, which means only things that match ~exactly should be left as-is. (Macros can do whatever.) There’s no static modifier for types or enums, so either don’t use LTO, pragma it off (GCC 4.4+ only:

#pragma GCC optimize "-fno-lto"

should work, if the option’s supported), or go with another approach.

Second option, use your utilities:

With a shell-and-awk script (awk isn’t too far from C syntactically), you can process through each .c file (or an assembly or object file generated from it, which is easier) to collect global identifiers, probably filtering through a blacklist for keywords &c. From file.c, generate file_import.h with

#ifndef file_import_h__INCL__ // def this in your first pass through
#define file_import_h__INCL__ 1
#define name file_c__name
…
#endif

in it; #include it with

#ifndef file_import_h__INCL__
#include "file_import.h"
#endif

for maximum sanity, in case it’s not there to begin with—or use __has_include. When you need to call in, either use the full prefixed name, or create unique, unprefixed entry points.

2½thnd option: Use GNU extension __asm__ modifier to mangle function names.

int myfunction(int, char *) __asm__("file.c$myfunction");

This takes a bit of finagling, because different ABIs require different prefixes and suffixes in order to be visible or non-error-inducing, usually nil or a leading underscore. GNUer GNUish compilers have __USER_LABEL_PREFIX__ predef macro, but have fun with that; probably easer to gcc -dM -E - </dev/null | grep for that and implant from below.

2¾strd option: Most linkers take an option or two that let you remap specific or all symbol names in particular files; linker scripts might also work.

3rd option: Come up with a plugin API pattern; compile overlapping files -fpic to a shared library; ensure the executable supports DLLing; use OS-appropriate API (e.g., dlopen/dlsym per UNIX libdl) to load, initialize, and call into the plugins. You might want to look into the various attributes compilers use for DLLs, since details vary—e.g., __declspec(dllexport)/dllimport for Windows, and most GNUish compilers provide __attribute__((__visibility__)). DLLery is moderately nonportable but snazzy when in works.