13

My C++ program needs to use an external C library. Therefore, I'm using the

extern "C"
{
   #include <library_header.h>
}

syntax for every module I need to use.

It worked fine until now. A module is using the this name for some variables in one of its header file. The C library itself is compiling fine because, from what I know, this has never been a keyword in C.

But despite my usage of the extern "C" syntax, I'm getting errors from my C++ program when I include that header file.

If I rename every this in that C library header file with something like _this, everything seems to work fine.

The question is:

Shouldn't the extern "C" syntax be enough for backward compatibility, at least at syntax level, for an header file? Is this an issue with the compiler?

nyarlathotep108
  • 5,275
  • 2
  • 26
  • 64

4 Answers4

14

Shouldn't the extern "C" syntax be enough for backward compatibility, at least at syntax level, for an header file? Is this an issue with the compiler?

No. Extern "C" is for linking - specifically the policy used for generated symbol names ("name mangling") and the calling convention (what assembly will be generated to call an API and stack parameter values) - not compilation.

The problem you have is not limited to the this keyword. In our current code base, we are porting some code to C++ and we have constructs like these:

struct Something {
    char *value;
    char class[20]; // <-- bad bad code!
};

This works fine in C code, but (like you) we are forced to rename to be able to compile as C++.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • 6
    Pro-tip: Write C++ name compatible code when writing C libraries. Saves a lot of headache for other developers. – Cole Tobin Sep 03 '14 at 14:29
  • RE: your class problem, note the hacky-yet-still-likely-supported-by-your-compiler workaround [in my answer](http://stackoverflow.com/a/25647229/211160). Of course, don't do that, fix it! :-) – HostileFork says dont trust SE Sep 03 '14 at 15:01
  • Ok, this is entirely clear now. As you said, solved the "this" keyword issue via renaming, it started complaining with int/bool conversions, and other main C/C++ core language differences. I was thinking "since is extern "C" it will compile using C specs", and I was totally wrong. Thanks! – nyarlathotep108 Sep 03 '14 at 15:20
  • 6
    @ColeJohnson: That's usually good advice for headers, bad advice for the implementation. You *want* C code to break quickly and break badly if somebody tries to compile it with a C++ compiler. Introducing subtle errors because they're different languages with different semantics is much worse. – R.. GitHub STOP HELPING ICE Sep 03 '14 at 17:08
  • 1
    @R.. You are correct. I was referencing headers. I just figured it was implied; guess not. Implementation is a whole different thing, I agree. – Cole Tobin Sep 03 '14 at 17:19
  • @R.. Er...I agree with your general point that if there is some grey area of compatibility, you should seek to rout it out as quickly and vocally as possible in a recompilation. But that has nothing whatsoever to do with C programmers actively naming struct fields `class` and global variables `this`. – HostileFork says dont trust SE Sep 03 '14 at 20:56
5

Strangely enough, many compilers don't forcibly disallow keyword redefinition through the preprocessor:

#include <iostream>

// temporary redefinition to compile code abusing the "this" keyword
#define cppThis this
#define this thisFunction

int this() {
    return 1020;
}

int that() {
   return this();
}

// put the C++ definition back so you can use it
#undef this
#define this cppThis

struct DumpThat {
    int dump() {
       std::cout << that();
    }
    DumpThat() {
       this->dump();
    }
};

int main ()
{
    DumpThat dt;
}

So if you're up against a wall, that could let you compile a file written to C assumptions that you cannot change.

It will not--however--allow you to get a linker name of "this". There might be linkers that let you do some kind of remapping of names to help avoid collisions. A side-effect of that might be they allow you to say thisFunction -> this, and not have a problem with the right hand side of the mapping being a keyword.

In any case...the better answer if you can change it is...change it!

Community
  • 1
  • 1
  • 1
    I don't think it's strange, or that a compiler has any choice. The preprocessor step is defined at token level. In that stage, tokens haven't been parsed to keywords yet, so you can't keyword tokens differently than non-keyword tokens. – MSalters Sep 03 '14 at 15:10
  • 2
    @MSalters See [Keywords redefinition in C / C++](http://stackoverflow.com/q/12286691/211160)...one could certainly blacklist the words from preprocessor use. – HostileFork says dont trust SE Sep 03 '14 at 15:17
  • Ow, right - C only forbids it in phase 7/8, after preprocessing phase 4 (see md5's answer) but in C++ it's banned outright (per the linked question). But `this` wouldn't be a keyword in C anyway. So instead of conditionally redefining it for C++, you should conditionally redefine it for C. – MSalters Sep 03 '14 at 18:18
  • This is a nice solution that could solve the general "keyword" problem, but actually it won't solve in general all the C/C++ differences. Guess it is a good thing that we can edit header files, also those ones from external libraries. – nyarlathotep108 Sep 04 '14 at 07:27
2

If extern "C" allowed you to use C++ keywords as symbols, the compiler would have to resolve them somehow outside of the extern "C" sections. For example:

extern "C" {
    int * this;  //global variable
    typedef int class;
}


int MyClass::MyFunction() { return *this; }  //what does this mean?
                                             //MyClass could have a cast operator
class MyOtherClass;  //forward declaration or a typedef'ed int?
IronMensan
  • 6,761
  • 1
  • 26
  • 35
1

Could you be more explicit about "using the this name for some variables in one of its header files"?

Is it really a variable or is it a parameter in a function prototype?

If it is the latter, you don't have a real problem because C (and C++) prototypes identify parameters by position (and type) and the names are optional. You could have a different version of the prototype, eg:

#ifdef __cplusplus
  extern "C" {
   void aFunc(int);
  }
#else
 void aFunc(int this);
#endif

Remember there is nothing magic about header files - they just provide code which is lexically included in at the point of #include - as if you copied and pasted them in.

So you can have your own copy of a library header which does tricks like the above, just becoming a maintenance issue to ensure you track what happens in the original header. If this was likely to become an issue, add a script as a build step which runs a diff against the original and ensures the only point of difference is your workaround code.

Andy Dent
  • 17,578
  • 6
  • 88
  • 115