Asked  7 Months ago    Answers:  5   Viewed   195 times

I'm working on a project that has a lot of legacy C code. We've started writing in C++, with the intent to eventually convert the legacy code, as well. I'm a little confused about how the C and C++ interact. I understand that by wrapping the C code with extern "C" the C++ compiler will not mangle the C code's names, but I'm not entirely sure how to implement this.

So, at the top of each C header file (after the include guards), we have

#ifdef __cplusplus
extern "C" {

and at the bottom, we write

#ifdef __cplusplus

In between the two, we have all of our includes, typedefs, and function prototypes. I have a few questions, to see if I'm understanding this correctly:

  1. If I have a C++ file A.hh which includes a C header file B.h, includes another C header file C.h, how does this work? I think that when the compiler steps into B.h, __cplusplus will be defined, so it will wrap the code with extern "C" (and __cplusplus will not be defined inside this block). So, when it steps into C.h, __cplusplus will not be defined and the code will not be wrapped in extern "C". Is this correct?

  2. Is there anything wrong with wrapping a piece of code with extern "C" { extern "C" { .. } }? What will the second extern "C" do?

  3. We don't put this wrapper around the .c files, just the .h files. So, what happens if a function doesn't have a prototype? Does the compiler think that it's a C++ function?

  4. We are also using some third-party code which is written in C, and does not have this sort of wrapper around it. Any time I include a header from that library, I've been putting an extern "C" around the #include. Is this the right way to deal with that?

  5. Finally, is this set up a good idea? Is there anything else we should do? We're going to be mixing C and C++ for the foreseeable future, and I want to make sure we're covering all our bases.



extern "C" doesn't really change the way that the compiler reads the code. If your code is in a .c file, it will be compiled as C, if it is in a .cpp file, it will be compiled as C++ (unless you do something strange to your configuration).

What extern "C" does is affect linkage. C++ functions, when compiled, have their names mangled -- this is what makes overloading possible. The function name gets modified based on the types and number of parameters, so that two functions with the same name will have different symbol names.

Code inside an extern "C" is still C++ code. There are limitations on what you can do in an extern "C" block, but they're all about linkage. You can't define any new symbols that can't be built with C linkage. That means no classes or templates, for example.

extern "C" blocks nest nicely. There's also extern "C++" if you find yourself hopelessly trapped inside of extern "C" regions, but it isn't such a good idea from a cleanliness perspective.

Now, specifically regarding your numbered questions:

Regarding #1: __cplusplus will stay defined inside of extern "C" blocks. This doesn't matter, though, since the blocks should nest neatly.

Regarding #2: __cplusplus will be defined for any compilation unit that is being run through the C++ compiler. Generally, that means .cpp files and any files being included by that .cpp file. The same .h (or .hh or .hpp or what-have-you) could be interpreted as C or C++ at different times, if different compilation units include them. If you want the prototypes in the .h file to refer to C symbol names, then they must have extern "C" when being interpreted as C++, and they should not have extern "C" when being interpreted as C -- hence the #ifdef __cplusplus checking.

To answer your question #3: functions without prototypes will have C++ linkage if they are in .cpp files and not inside of an extern "C" block. This is fine, though, because if it has no prototype, it can only be called by other functions in the same file, and then you don't generally care what the linkage looks like, because you aren't planning on having that function be called by anything outside the same compilation unit anyway.

For #4, you've got it exactly. If you are including a header for code that has C linkage (such as code that was compiled by a C compiler), then you must extern "C" the header -- that way you will be able to link with the library. (Otherwise, your linker would be looking for functions with names like _Z1hic when you were looking for void h(int, char)

5: This sort of mixing is a common reason to use extern "C", and I don't see anything wrong with doing it this way -- just make sure you understand what you are doing.

Tuesday, June 1, 2021
answered 7 Months ago

The first bitmap is not garbage collected when you decode the second one. Garbage Collector will do it later whenever it decides. If you want to free memory ASAP you should call recycle() just before decoding the second bitmap.

If you want to load really big image you should resample it. Here's an example: Strange out of memory issue while loading an image to a Bitmap object.

Sunday, June 6, 2021
answered 7 Months ago

It may help to think of the relationship between a "directive" and being "given directions" (i.e. orders). "preprocessor directives" are directions to the preprocessor about changes it should make to the code before the later stages of compilation kick in.

But, what's the preprocessor? Well, its name reflects that it processes the source code before the "main" stages of compilation. It's simply there to process the textual source code, modifying it in various ways. The preprocessor doesn't even understand the tokens it operates on - it has no notion of types or variables, classes or functions - it's all just quoted- and/or parentheses- grouped, comma- and/or whitespace separated text to be manhandled. This extra process gives more flexibility in selecting, combining and even generating parts of the program.

EDIT addressing @SWEngineer's comment: Many people find it helpful to think of the preprocessor as a separate program that modifies the C++ program, then gives its output to the "real" C++ compiler (this is pretty much the way it used to be). When the preprocessor sees #include <iostream> it thinks "ahhha - this is something I understand, I'm going to take care of this and not just pass it through blindly to the C++ compiler". So, it searches a number of directories (some standard ones like /usr/include and wherever the compiler installed its own headers, as well as others specified using -I on the command line) looking for a file called "iostream". When it finds it, it then replaces the line in the input program saying "#include " with the complete contents of the file called "iostream", adding the result to the output. BUT, it then moves to the first line it read from the "iostream" file, looking for more directives that it understands.

So, the preprocessor is very simple. It can understand #include, #define, #if/#elif/#endif, #ifdef and $ifndef, #warning and #error, but not much else. It doesn't have a clue what an "int" is, a template, a class, or any of that "real" C++ stuff. It's more like some automated editor that cuts and pastes parts of files and code around, preparing the program that the C++ compiler proper will eventually see and process. The preprocessor is still very useful, because it knows how to find parts of the program in all those different directories (the next stage in compilation doesn't need to know anything about that), and it can remove code that might work on some other computer system but wouldn't be valid on the one in use. It can also allow the program to use short, concise macro statements that generate a lot of real C++ code, making the program more manageable.

Friday, August 6, 2021
answered 4 Months ago

Looking at similar question it seems like Eclipse CDT has exactly the functionality you need and the other question actually tells where you can set your ifdefs.

Emacs has something similar in form of hide-ifdef-mode.

But you can also try to use IDE-agnostic solution like running the code through unifdef and working with the result. If you just need to read the code, it's almost perfect solution. If you need to make some changes, things become a bit more complicated, but you can use git then to manage changes, like:

  • import whole code base into git
  • run through unifdef
  • commit the result, that's the basis for your patches
  • work with the code base in whatever IDE/editor you prefer, commiting changes as usual
  • if there is a need to produce patches for original code base, just checkout the original import commit and cherry-pick (or rebase) your patches from your branch (of course there is a chance for conflict, but it should be quite easy to resolve as you know your intended change for the code from the patch and you just need to adjust for ifdefs)
  • if there is a need to update code base, just start from original import, apply an update, commit that, run through unifdef, commit that and then rebase your changes on top of that

Of course, whether this approach will work or not depends on particular code base and what you're going to do with it, but it can be useful in some scenarios.

Wednesday, October 13, 2021
answered 2 Months ago

Pivot is the point around which it rotates (like putting a pin in a photo). If your 'from' and 'to' pivots aren't the same then you are not just rotating around a set point, you're rotating a bit, then changing the pins location and rotating a bit more for each step (causing a skew).

Just in case you don't know: Android coordinates start at top left not bottom left.

I think all you want in that set is

<set xmlns:android=""
     <rotate android:fromDegrees="0"
            android:pivotY="30%" />  
     <!-- I believe this, below will the the correct slide you need -->
     <translate android:fromXDelta="0" 
                android:toXDelta="-100%" />

Which will rotate it 130 degrees around the pin at [100%, 30%] (x being the maximum value , i.e right edge of the screen, and y being 30% of the way down the screen) and at the same time slide it right until it's 100% (of view width) to the right of it's starting position

Friday, November 26, 2021
answered 2 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :