dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

questions to C libs also for g++

Fri Sep 25, 2020 11:10 am

hello,
I, some .h files I just found things like

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int bmp180Setup (const int pinBase) ;
#ifdef __cplusplus
}
#endif
what do those obfuscated nested #ifdef... #endifs with "extern C" in nested {...} with extra #endif do or mean?
Last edited by dsyleixa123 on Fri Sep 25, 2020 11:56 am, edited 2 times in total.

User avatar
jahboater
Posts: 6274
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: questions to C libs also for g++

Fri Sep 25, 2020 11:30 am

dsyleixa123 wrote:
Fri Sep 25, 2020 11:10 am
what do those obfuscated nested #ifdef... #endifs with "extern C" in nested {...} with extra #endif do or mean?
It just includes the extern "C" only if the program is being compiled as C++.
extern "C" is not valid C syntax.
This:

Code: Select all

extern "C" {#endif
is probably a typo!
Pi4 8GB running PIOS64 Lite

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 11:51 am

yes, it was a c+p failure.
But in this .h file I do not understand the entire logic and sense behind and in it.

why 2x #ifdef...#endif in either directive with the additional { } etc in between?

Code: Select all

#ifdef __cplusplus //1st  #ifdef
extern "C" {   // <<<<< {
#endif //1st  #endif in the { }
(...)  // "extern" function name of the .c file
#ifdef __cplusplus // 2nd #ifdef  in the { }
}           // <<<<< }
#endif //2nd  #endif outside the { }
and why an "extern "C" " anyway?

User avatar
jahboater
Posts: 6274
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: questions to C libs also for g++

Fri Sep 25, 2020 12:21 pm

Extern C tells the compiler not to do any C++ name mangling on the C symbols.
The "#ifdef __cplusplus" says only do it for a C++ compilation (because __cplusplus is only defined by the C++ compiler). It will be ignored for a C compilation, enabling the code to be compiled as both C and C++.

It encloses a block in the usual { ... } way, and any number of #includes or whatever may go in between.
Just as anyware in C/C++, the curly's are only needed for multiple statements. When enclosing a #include of course, because any number of statements may be in the include file, the { } are always used.

The second #ifdef/#endif here encloses the final closing }.
If it were not enclosed in the matching #ifdef/#endif, then the compiler would complain about finding a closing } with no opening one!

More simply, anywhere in C you could write:

Code: Select all

#if 1
{
#endif
later in the code:

Code: Select all

#if 1
}
#endif
You can see that the curly's must match up.
Pi4 8GB running PIOS64 Lite

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 1:06 pm

ok, thanks, let me see, i still don't get it:
for

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
why not simply:

Code: Select all

#ifdef __cplusplus
extern "C" 
#endif

int ads1115Setup (int pinBase, int i2cAddress) ;  //<<< refer to this function just like it's available in the related .c file


User avatar
jahboater
Posts: 6274
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: questions to C libs also for g++

Fri Sep 25, 2020 2:39 pm

Yes, that aught to work (I've not tried it) because its a single statement and doesn't need the curly's.
(as my second paragraph above).

You might still need the "extern" of course:

Code: Select all

#ifdef __cplusplus
extern "C" 
#endif
extern int ads1115Setup( int pinBase, int i2cAddress );
Pi4 8GB running PIOS64 Lite

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 4:40 pm

why do I need the "extern" for the function? Finally it's the same here in the ads1115.h file as in the ads1115.c file? I never observed this "extern" thing in other libs (e.g., in Arduino C++ libs)

User avatar
Paeryn
Posts: 3077
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: questions to C libs also for g++

Fri Sep 25, 2020 4:43 pm

jahboater wrote:
Fri Sep 25, 2020 2:39 pm
You might still need the "extern" of course:

Code: Select all

#ifdef __cplusplus
extern "C" 
#endif
extern int ads1115Setup( int pinBase, int i2cAddress );
No, you can't do that as when compiled as C++ you would end up with

Code: Select all

extern "C" extern int ads1115Setup( int pinBase, int i2cAddress ):
You can't have extern "C" extern as the second extern makes no sense. It works inside a block because the extern on the function is lexically separate from the extern "C" {
She who travels light — forgot something.
Please note that my name doesn't start with the @ character so can people please stop writing it as if it does!

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 4:49 pm

then I'm back at the very start.
I absolutely don't understand what and how this double nested preprocessor #ifdef and #endif and brackets and double "extern" things do and work.
Even intendations make no sense at all.

Code: Select all

#ifdef __cplusplus
   extern "C" {
      #endif
         extern int ads1115Setup (int pinBase, int i2cAddress) ;
            #ifdef __cplusplus
   }
#endif
:?: :?: :o :shock: :( :?: :?:

User avatar
jahboater
Posts: 6274
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: questions to C libs also for g++

Fri Sep 25, 2020 5:02 pm

dsyleixa123 wrote:
Fri Sep 25, 2020 4:40 pm
why do I need the "extern" for the function? Finally it's the same here in the ads1115.h file as in the ads1115.c file? I never observed this "extern" thing in other libs (e.g., in Arduino C++ libs)
To say that it might be defined in another source file.
Look up the extern keyword in google.
Pi4 8GB running PIOS64 Lite

plugwash
Forum Moderator
Forum Moderator
Posts: 3691
Joined: Wed Dec 28, 2011 11:45 pm

Re: questions to C libs also for g++

Fri Sep 25, 2020 5:08 pm

This is a structure you will generally find in the include files for libraries that are intended to be called from either C or C++. You will generally not find it in headers that are only intended to be used in C++.

The underlying reason for this is because C++ supports function overloading. In order to do this the C++ compiler mangles the names of functions when generating the binary object files. So in C a function like int foo(int bar, int baz); would have a name of foo in the binary object file but in C++ the same function would have a name like _Z3fooii (the exact name may vary between platforms) in the binary object file.

This creates a compatibility issue though, if you declare and define your functions normally then code written in C won't be able to call code written in C++ and vice-versa.

To get around this C++ has extern "C" blocks. If the first definition (within a compilation unit) of a function is inside such a block then it's name will not be mangled. This allows for calls between C and C++ to be linked successfully (in either direction).

But a C compiler has no idea what extern "C" means. So if you just surround the contents of your header with in an extern "C" block then your header will compile in C++ but not in C. Most people don't want to maintain seperate headers for calling their library from C and C++.

The solution to this is to use #ifdef __cplusplus to surround the start and end of the extern "C" block. So when the header is compiled by a C++ compiler the declarations are included in an extern "C" block and when the header is compiled by a C compiler they are just included normally.

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 5:20 pm

ok, thanks, there now arise many new ideas and aspects, so one by one:
1)
So in C a function like int foo(int bar, int baz); would have a name of foo in the binary object file but in C++ the same function would have a name like _Z3fooii (the exact name may vary between platforms) in the binary object file.
where does the _Z3fooii come from, and what has that to do with my proprietary declared foo() function as a _Z3fooii function is never declared in my source files so never can interfere with either proprietary one?

User avatar
jahboater
Posts: 6274
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: questions to C libs also for g++

Fri Sep 25, 2020 6:01 pm

dsyleixa123 wrote:
Fri Sep 25, 2020 5:20 pm
where does the _Z3fooii come from, and what has that to do with my proprietary declared foo() function as a _Z3fooii function is never declared in my source files so never can interfere with either proprietary one?
Re-read what @plugwash explained above.
Its called name mangling.
Pi4 8GB running PIOS64 Lite

User avatar
Paeryn
Posts: 3077
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: questions to C libs also for g++

Fri Sep 25, 2020 6:02 pm

dsyleixa123 wrote:
Fri Sep 25, 2020 4:49 pm
then I#m back at the very start.
I absolutely don't understand what and how this double nested preprocessor #ifdef and #endif and brackets and double "extern" things do and work.

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
As jahboater said, when compiled as C those #ifdef...#endif pairs effectively hide the two lines of C++ code, but when compiled as C++ they effectively enclose the lines between the two in an extern "C" {...} block.

What might be confusing you is that extern when used in as

Code: Select all

extern int ads1115Setup(int pinBase, int i2cAddress);
is declaring the storage-class of the function ads1115Setup to be external, whereas in

Code: Select all

extern "C" {...}
extern is being used to declare C language linkage (as opposed to the default of C++ language linkage) for identifiers declared in the block. This use of extern is not understood by C, it's only valid in C++ and is used by the compiler to specify how an identifier will be linked. G++ uses name mangling whereby a function's name is extended with type information when generating its link name, this means that without the extern "C" the above function's link name in g++ will be

Code: Select all

_Z12ads1115Setupii
which won't match up with the link name that C uses which will be just the function's name i.e.

Code: Select all

ads1115Setup
This name mangling that C++ does is largely transparent to the programmer (the compilers of old used to give you error messages containing mangled names which is why I can translate g++'s mangled names), you just have to know that if you want to link to your C++ function from another language like C, or you want to use a function from a C library then you need to tell the compiler that it is to use C linkage for that function (via extern "C").

Edit: to include the question re _Z3fooii.
_Z3fooii is g++'s mangled name for a C++ function declared something like (void might not be what you used for the return type, it isn't encoded in the mangled name)

Code: Select all

void foo( int, int );
That is, a function named foo that takes two ints, the names of the parameters don't matter and the return type of the function isn't encoded.
She who travels light — forgot something.
Please note that my name doesn't start with the @ character so can people please stop writing it as if it does!

swampdog
Posts: 413
Joined: Fri Dec 04, 2015 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 6:20 pm

Code: Select all

foo@pi18:/wrk/TT $ cat c.x
/*?are we a C++ compiler?*/
#ifdef __cplusplus
extern "C" {
#endif

void	foo(void);

//?are we a C++ compiler?
#ifdef __cplusplus
}
#endif

int
main(void)
{
 foo();
 return 0;
}

Code: Select all

foo@pi18:/wrk/TT $ ln -s c.x c.c ; ln -s c.x c.cc
foo@pi18:/wrk/TT $ ls -l
total 4
lrwxrwxrwx 1 foo foo   3 Sep 25 18:14 c.c -> c.x
lrwxrwxrwx 1 foo foo   3 Sep 25 18:14 c.cc -> c.x
-rw-r--r-- 1 foo foo 178 Sep 25 18:08 c.x
You'll note the output differs..

Code: Select all

$ gcc -E c.c
$ g++ -E c.cc
As @jarboater mentions this is because of name mangling..

Code: Select all

foo@pi18:/wrk/TT $ gcc c.c
/usr/bin/ld: /tmp/cc63Eeqp.o: in function `main':
c.c:(.text+0x8): undefined reference to `foo'
collect2: error: ld returned 1 exit status
foo@pi18:/wrk/TT $ g++ c.cc
/usr/bin/ld: /tmp/ccUTpcHE.o: in function `main':
c.cc:(.text+0x8): undefined reference to `foo'
collect2: error: ld returned 1 exit status
..but all is not what it seems above.

Code: Select all

foo@pi18:/wrk/TT $ gcc -c c.c && nm -A c.o
c.o:         U foo
c.o:00000000 T main
foo@pi18:/wrk/TT $ g++ -c c.cc && nm -A c.o
c.o:         U __aeabi_unwind_cpp_pr1
c.o:         U foo
c.o:00000000 T main
If you remove all the "extern C" stuff..

Code: Select all

foo@pi18:/wrk/TT $ cat c.x
void	foo(void);

int
main(void)
{
 foo();
 return 0;
}
Now this happens..

Code: Select all

foo@pi18:/wrk/TT $ g++ -c c.cc && nm -A c.o
c.o:         U __aeabi_unwind_cpp_pr1
c.o:00000000 T main
c.o:         U _Z3foov
That "_Z3foov" is a mangled function name.

Code: Select all

foo@pi18:/wrk/TT $ cat c.cpp
void	foo(void);
int	foo(char);
char*	foo(int);

int
main(void)
{
 foo();
 foo('a');
 foo(0);
 return 0;
}

Code: Select all

foo@pi18:/wrk/TT $ g++ c.cpp
/usr/bin/ld: /tmp/ccBEeHSq.o: in function `main':
c.cpp:(.text+0x8): undefined reference to `foo()'
/usr/bin/ld: c.cpp:(.text+0x10): undefined reference to `foo(char)'
/usr/bin/ld: c.cpp:(.text+0x18): undefined reference to `foo(int)'
collect2: error: ld returned 1 exit status

Code: Select all

foo@pi18:/wrk/TT $ g++ -c c.cpp && nm -A c.o
c.o:         U __aeabi_unwind_cpp_pr1
c.o:00000000 T main
c.o:         U _Z3fooc
c.o:         U _Z3fooi
c.o:         U _Z3foov
C++ has "mangled" (ie: modified the function name) such that it can call three different functions, all called "foo" as far as the programmer is concerned but each of which take different arguments. It knows which one to call by how you use it in your code. C cannot do this: there is a single function "foo".

The "extern C" is how we tell C++ it is to call a C function and not mangle the name. However, that is not valid C so it needs to be omitted when we're compiling code which can be C. A C++ compiler automatically defines "__cplusplus" but a C compiler does not.

In case you're not aware, neither a C compiler nor a C++ compiler compile the code you write. They first run the preprocessor and it is that output which is actually compiled. That's the "-E" option above (preprocessor only). I deliberately made an example with no headers because the output would have been huge. ;-)

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 8:40 pm

thank you all for your efforts, but my english is too poor to understand what "mangling" means or does for which purpose exactly and why this collides with my (actuall, Gordon's) legal C lib code, and all he foos don't make it more clear, tbh... :shock: :?
ok, let it be.

But apart from mangling, can anyone please explain me what this obfuscated weird block means or does?

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
I mean, line by line, and which lines are alligned to which other lines to sort of combined "blocks" - I don't recognize any valid code syntax in there, it all looks totally cofusing and senseless, just if one rolls letters by dices and places them unsorted on Scrabble fields...

plugwash
Forum Moderator
Forum Moderator
Posts: 3691
Joined: Wed Dec 28, 2011 11:45 pm

Re: questions to C libs also for g++

Fri Sep 25, 2020 9:49 pm

dsyleixa123 wrote:
Fri Sep 25, 2020 8:40 pm
thank you all for your efforts, but my english is too poor to understand what "mangling" means or does for which purpose exactly
Every function that is accessible from outside the compilation unit must have a unique name in the binary object file. These names are used by the linker to match up the declarations of functions in one compilation unit to definitions in another compilation unit.

For C this is no problem, C doesn't have overloading, namespaces classes etc so the function name in the source code is simply used directly in the binary object file.

In C++ there can be many functions with the same name, differentiated either by their argument types, by their namespace, by which class they are part of and so-on. So the function name in the source code cannot normally be used directly in the binary object file.

"name mangling" is the process by which a C++ compiler takes the name of a function and combines it with other information (parameter types, namespace if any, class if any) to produce a unique identifier to identify the function in the binary object file.
But apart from mangling, can anyone please explain me what this obfuscated weird block
While you may find it weird as a newcomer, this is a standard idiom in header files that are designed to be used in both C and C++ and there isn't really a better way of doing the same thing.
and which lines are alligned to which other lines to sort of combined "blocks"
What you need to appreciate here is that the block structure of the pre-proceseor and the compiler are separate. The pre-processor doesn't care about the compiler's blocks and the compiler doesn't care about the pre-processors blocks.

#ifdef __cplusplus and #endif form a preprocessor conditional block. If __cplusplus is defined then the code inside the preprocessor block will be passed on to the compiler. If it is not defined then the code in the block will be skipped. When the preprocessor is invoked by a C++ compiler __cplusplus will be defined, but when the preprocessor is invoked by a C compiler it will not.

So when processed by the preprocessor invoked by the C++ compiler your code translates to

Code: Select all

extern "C" {
extern int ads1115Setup (int pinBase, int i2cAddress) ;
}
Whereas when processed by the preprocessor invoked by the C compiler it translates to

Code: Select all

extern int ads1115Setup (int pinBase, int i2cAddress) ;
The extern "C" block tells the C++ compiler that the names of functions first declared inside the block should not be mangled. No similar block is needed for the C compiler because C compilers do not mangle names.

extern int ads1115Setup (int pinBase, int i2cAddress) ; declares a function called ads1115Setup which takes two parameters both of type int. The extern is unnecessary. For global variables it is used to differentiate between merely declaring a variable and defining a variable, but for functions it is unnecessary as the compiler can determine whether you are just declaring or both declaring and defining a function based on whether or not you include a block of code.

cleverca22
Posts: 1864
Joined: Sat Aug 18, 2012 2:33 pm

Re: questions to C libs also for g++

Fri Sep 25, 2020 9:55 pm

another tool that can be useful for figuring out mangled names is:

Code: Select all

[nix-shell:~]# c++filt _Z3fooii
foo(int, int)
it takes a mangled name, and will turn it the c code it represents

User avatar
jahboater
Posts: 6274
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: questions to C libs also for g++

Fri Sep 25, 2020 10:48 pm

dsyleixa123 wrote:
Fri Sep 25, 2020 8:40 pm
But apart from mangling, can anyone please explain me what this obfuscated weird block means or does?

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
I mean, line by line, and which lines are alligned to which other lines to sort of combined "blocks" - I don't recognize any valid code syntax in there, it all looks totally cofusing and senseless, just if one rolls letters by dices and places them unsorted on Scrabble fields...
This has already been explained several times by several different people.

I'm done.
Pi4 8GB running PIOS64 Lite

User avatar
Paeryn
Posts: 3077
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: questions to C libs also for g++

Fri Sep 25, 2020 10:51 pm

dsyleixa123 wrote:
Fri Sep 25, 2020 8:40 pm
thank you all for your efforts, but my english is too poor to understand what "mangling" means or does for which purpose exactly and why this collides with my (actuall, Gordon's) legal C lib code, and all he foos don't make it more clear, tbh... :shock: :?
ok, let it be.

But apart from mangling, can anyone please explain me what this obfuscated weird block means or does?

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
I mean, line by line, and which lines are alligned to which other lines to sort of combined "blocks" - I don't recognize any valid code syntax in there, it all looks totally cofusing and senseless, just if one rolls letters by dices and places them unsorted on Scrabble fields...
Here goes...
#ifdef and #endif are standard C-Pre-Processor directives, if the identifier is defined then the code in the block will be parsed by the compiler. If the identifier isn't defined then the code in the block will be ignored, think of it like selectively commenting out code.

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
The C++ compiler will implicitly define __cplusplus so the line extern "C" { will be parsed by the compiler.
The C compiler however will not define __cpluscplus so that line will be not be parsed by the compiler.
Same goes for the other #ifdef...#endif block, so effectively when compiled as C you just end up with one line,

Code: Select all

extern int ads1115Setup (int pinBase, int i2cAddress) ;
But when compiled as C++ you get three lines (I've added indentation to hopefully make it clearer),

Code: Select all

extern "C" {
    extern int ads1115Setup (int pinBase, int i2cAddress) ;
}
What this tells the C++ compiler is that
  • The function ads1115Setup will be linked as a C function and not as a C++ function (because it is in an extern "C" {...} block). This doesn't mean that the function is necessarily compiled with C, it just means that it will be called using C conventions rather than C++.
  • It has external linkage (from the extern on the line declaring the function). Though functions at file scope have external linkage by default so you don't technically need this extern, only case I can think of where it matters is when using extern inline
C++ name mangling just means that C++ changes the names of functions by some means when generating object files. I'll use g++'s name mangling scheme, other compilers on other platforms may use an entirely different name mangling scheme, in fact they will do so unless their compiler generates object files that are meant to be directly compatible. Using this function declaration

Code: Select all

int add( int x, int y);
The C compiler will generate a symbol named add for the function and whenever you call the function it generates a call to add.
The C++ compiler will instead generate a symbol named _Z3addii for the function and whenever you call the function add it actually calls the function _Z3addii
Hopefully you can see that if the function is in an object file that was generated by a C compiler then trying to use that function from C++ wouldn't work because the name differs. This is why you have to tell C++ to use the C naming system for that function.

C++ uses name mangling because the language allows function overloading, simplistic example:

Code: Select all

#include <cstdio>
int add( int x, int y)
{
    int z = x + y;
    printf("integer add %d to %d to get %d\n", x, y, z);
    return z;
}

float add( float x, float y)
{
    float z = x + y;
    printf("floating point add %f to %f to get %f\n", x, y, z);
    return z;
}

int main(void)
{
    int i = add( 1, 2 );
    float f = add( 1.1, 2.2 );
}
If C++ didn't mangle function names then there would be a problem, there are two separate functions but both have the same name. With name mangling there isn't a problem because C++ changes the function names to _Z3addii for the integer add() and to _Z3addff for the float add().

You can't do this in C, nor can you in C++ if you declared both the functions with extern "C". You can still overload a function in C++ when using extern "C" so long as only one version is declared with C linkage.
She who travels light — forgot something.
Please note that my name doesn't start with the @ character so can people please stop writing it as if it does!

swampdog
Posts: 413
Joined: Fri Dec 04, 2015 11:22 am

Re: questions to C libs also for g++

Fri Sep 25, 2020 11:03 pm

If you run my "c.x" post example things might become clearer. I omitted the output of "gcc -E c.c" and "g++ -E c.cc" hoping you'd type in my code and all the commands in that post to see for yourself. Go grab a drink, take a deep breath and now for some C compiler history.

A C compiler has the notion (idea) of a "compilation unit". You feed in text and the output is a compiled object file. Hopefully that input text is valid C.

Code: Select all

+---------------+    +----------------+    +-----------------------+
| C source file | -> | preprocessor   |    | compilation unit      |
+---------------+    | (cpp) (gcc -E) | -> | (temporary text file) |
                     +----------------+    +-----------------------+


+------------------+    +-------------+
| compilation unit | -> | object file |
+------------------+    | (gcc -c)    |
                        +-------------+

+--------+    +------------------+    +------------+
| linker | -> | all object files | -> | executable |
| (ld)   |    | (add libraries)  |    +------------+
+--------+    | (*.so and *.a    |
              +------------------+
Think of it as this..

Code: Select all

+--------------+
| header files | <- description of (system) capabilities
+--------------+

+--------------+
| object files | <- application or additional (system) capabilities
+--------------+

+---------------+
| library files | <- implementation of (system) capabilities
+---------------+
The linker (ld) is not part of gcc but gcc knows how to make use of it. No more about the linker.

The preprocessor (cpp) still exists but gcc has its own internal one (gcc -E) for many reasons. Note in what follows when I say "cpp" I really mean preprocessor (gcc -E) but it does not matter. We're trying to solve your confusion! ;-)


C was a glorified assembler and 'cpp' came about to save typing. You could write "#define FRED 74" and use "int a=FRED" in your code rather than "int a=74". All 'cpp' does is search for what it knows and replaces it. When it sees FRED it replaces it with 74 and so on. The output from 'cpp' is what the C compiler actually compiles. Then things got a bit more complex. Folks would write (say) "#define MIN(a,b) (((a)<(b))?(a):(b))" so when we write "x=MIN(30,20)" 'ccp' actually turns that into "x=(((30)<(20))?(30):(20))". A modern compiler can optimise that out to "x=20" but an old K&R C compiler would be so dumb it would generate all the assembler for the test. So dumb in fact that people would write low level C in assembler so trivial functions such as memcpy() and strcat() could be significantly faster.

That bought its own problem. Assembler isn't portable so 'cpp' needed a conditional operator (#ifdef) which could choose between using optimised assembler or generic C if the new target platform had no optimised assembler. If the #ifdef argument existed the stuff before the next #endif was sent to the compilation unit. If the argument did not exist, 'cpp' just sent an empty line for each line that would have been there had the argument existed. Thus we can write "gcc -DOOK" and if there's a "#ifdef OOK" in your source your source may be compiled differently.

Code: Select all

#ifdef FOO
echo FOO is set
#else
echo FOO is not set
#endif
^^^paste that into /tmp/z and "cpp /tmp/z". Now do "cpp -DFOO /tmp/z". Now try with "cpp -DFOO=7 /tmp/z". Note how FOO has been replaced. Note also we are not using a compiler. There is no difference here between it and some other text substitution command such as 'sed'.

Code: Select all

#ifdef __cplusplus    //only set by C++ compiler
extern "C" {          //C++ then emit "extern C {" to compilation unit
                      //    else emit blank line
#endif                //</#ifdef>
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus    //only set by C++ compiler
}                     //C++ then emit "}" to compilation unit
                      //    else emit blank line
#endif                //</#ifdef>
Result is C compiler sees "extern int ads1115Setup (int pinBase, int i2cAddress) ;" whereas C++ compiler sees "extern "C { extern int ads1115Setup (int pinBase, int i2cAddress) ; }". Now the same C header can serve both C and C++ compilers and the C++ compiler knows it needs to call a C function (and not a C++ mangled function).


It's no different to conditional header inclusion. All my headers (eg: sdApp.hpp) are wrapped like this..
#ifndef sdApp_SD
#define sdApp_SD sdApp_SD
//all my function prototypes
#endif //sdApp_SD
..which prevents that header from being pulled in more than once by a compilation unit.

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Sat Sep 26, 2020 8:18 am

ok, thanks really a lot for all your efforts. Probably I was not clear enough about the Obfuscations. Unfortunately I have to admit that all this preprocessor and compiler and mangle and intermediate temporary stuff is far beyond my mental and linguistic skills, tbh... :shock: :?

as to

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
I know what an #ifdef is and that it requires an #endif.
I understand the first #ifdef ...#endif block

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
means that if __cplusplus is defined, then
extern "C" {
will be noticed and processed. But what the heck does this line with the open bracket mean, and then nothing behind?
Because finally after that then the #endif terminates it, so this block is complete!

then, next, a function is called

Code: Select all

extern int ads1115Setup (int pinBase, int i2cAddress) ;
fine, and it's extern, also fine.

them next, another #ifdef ...#endif block:

Code: Select all

#ifdef __cplusplus
}
#endif
so just if __cplusplus is defined, then just process a single closed bracket? WTF...???

Isn't is possible to put all what has to be done for C++ in 1 single readable and comprehensible line or perhaps a couple of lines, in just 1 single #ifdef ...#endif block?
and else, what has to be done for native C?

Perhaps instead (what I can possibly suspect from Paeryn's post) something like, e.g.,

Code: Select all

#ifdef __cplusplus
   extern "C" { extern int ads1115Setup (int pinBase, int i2cAddress) ; }
#else
   extern int ads1115Setup (int pinBase, int i2cAddress) ; 
#endif
Last edited by dsyleixa123 on Sat Sep 26, 2020 8:42 am, edited 2 times in total.

markkuk
Posts: 233
Joined: Thu Mar 22, 2018 1:02 pm
Location: Finland

Re: questions to C libs also for g++

Sat Sep 26, 2020 8:39 am

dsyleixa123 wrote:
Sat Sep 26, 2020 8:18 am
But what the heck does this line with the open bracket mean, and then nothing behind?
It means that there must be a matching closing bracket inside a similar #ifdef/#endif block somewhere later.
dsyleixa123 wrote:
Sat Sep 26, 2020 8:18 am
then, next, a function is called

Code: Select all

extern int ads1115Setup (int pinBase, int i2cAddress) ;
No, a function is declared, nothing is called in a header (.h) file. https://www.cprogramming.com/declare_vs_define.html
dsyleixa123 wrote:
Sat Sep 26, 2020 8:18 am
them next, another #ifdef ...#endif block:

Code: Select all

#ifdef __cplusplus
}
#endif
so just if __cplusplus is defined, then just process a single closed bracket? WTF...???
That's the matching bracket for the opening bracket in the first #ifdef block.
dsyleixa123 wrote:
Sat Sep 26, 2020 8:18 am
Isn't is possible to put all what has to be done for C++ in 1 single readable and comprehensible line or perhaps a couple of lines, in just 1 single #ifdef ...#endif block?
That would require duplicating everything that's between the two #ifdef blocks in the original file, and would quickly cause maintenance problems especially in large files.
dsyleixa123 wrote:
Sat Sep 26, 2020 8:18 am
and else, what has to be done for native C?
Nothing.

dsyleixa123
Posts: 1025
Joined: Mon Jun 11, 2018 11:22 am

Re: questions to C libs also for g++

Sat Sep 26, 2020 8:46 am

So what about this
Perhaps instead (what I can possibly suspect from Paeryn's post) something like, e.g.,

Code: Select all

#ifdef __cplusplus
   extern "C" { extern int ads1115Setup (int pinBase, int i2cAddress) ; }
#else
   extern int ads1115Setup (int pinBase, int i2cAddress) ; 
#endif

User avatar
rpdom
Posts: 17705
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: questions to C libs also for g++

Sat Sep 26, 2020 9:41 am

dsyleixa123 wrote:
Sat Sep 26, 2020 8:46 am
So what about this
Perhaps instead (what I can possibly suspect from Paeryn's post) something like, e.g.,

Code: Select all

#ifdef __cplusplus
   extern "C" { extern int ads1115Setup (int pinBase, int i2cAddress) ; }
#else
   extern int ads1115Setup (int pinBase, int i2cAddress) ; 
#endif
It all adds up to the same thing.

If using C++ it will generate

Code: Select all

  extern "C" { extern int ads1115Setup (int pinBase, int i2cAddress) ; }
and if not, it will generate

Code: Select all

   extern int ads1115Setup (int pinBase, int i2cAddress) ; 
The

Code: Select all

#ifdef __cplusplus
extern "C" {
#endif
extern int ads1115Setup (int pinBase, int i2cAddress) ;
#ifdef __cplusplus
}
#endif
generates exactly the same thing.
Unreadable squiggle

Return to “C/C++”