2

I'm trying to compile a C codebase as C++, tweaking some of the includes. It uses strchr() on unsigned char pointers, e.g.:

#include <string.h>
#include <stdio.h>

// Cannot modify this file with any non-C-isms, but let's say an include can
// be changed (although all things being equal I'd rather not)
#include <SomethingICanChange.h>

int main(int argc, char* argv[]) {
    unsigned char b = 'B';
    unsigned char const * str = "ABC";
    unsigned char const * pos = strchr(str, b);
    if (pos) {
        printf("position is %d\n", pos - str);
    }
    return 0;
}

That causes an error in C++ (for reasons explained elsewhere)...even with -fpermissive.

test.c: In function ‘int main(int, char**)’:
test.c:6:33: warning: invalid conversion from ‘const char*’ to ‘const unsigned char*’ [-fpermissive]
test.c:7:46: error: call of overloaded ‘strchr(const unsigned char*&, unsigned char&)’ is ambiguous
test.c:7:46: note: candidates are:
In file included from test.c:1:0:
/usr/include/string.h:215:14: note: char* strchr(char*, int) <near match>
/usr/include/string.h:215:14: note:   no known conversion for argument 1 from ‘const unsigned char*’ to ‘char*’
/usr/include/string.h:217:22: note: const char* strchr(const char*, int) <near match>
/usr/include/string.h:217:22: note:   no known conversion for argument 1 from ‘const unsigned char*’ to ‘const char*’

Usually when faced with this kind of thing I go "strchr, yuck, just get rid of it entirely". But I don't want to modify these files myself, and this is C code that is being kept very portable to rather old platforms. They wouldn't be happy putting casts at callsites to appease C++...though if a "struchr" existed and was standard they'd probably use it.

I can also hack around it one way or another, like in SomethingICanChange.h I could add:

unsigned char const * strchr(unsigned char const * s, int c) {
    return (unsigned char const *)strchr((char const *)s, c);
}

That works with -fpermissive. But I wouldn't be able to check that overload into the C codebase (well, without an #ifdef). Just wondering if anyone has a better idea, I will pitch adding struchr if that's what people in this situation wind up doing (or whatever name is common if there's a common one).

  • 4
    "I'm trying to compile a C codebase as C++" -- that's basically an error, but if you're determined to do it then I think adding overloads in C++ that make the C functions callable the same ways in C++ as they are in C is the right thing to do. That will keep you going until you find C code that calls `strchr` on an `unsigned char*` but assigns the result to a `char*` -- then you just have to convince the C programmers that although that's *allowed* it's still not *right*, and they should change their code. – Steve Jessop Dec 16 '12 at 22:40
  • @SteveJessop It was at least at one time argued that a strength of C++ was it's ability to bring incremental benefit to old codebases. :-/ If that's true though I guess the overloading is the tool to use if no one else hitting this problem as established prior art. But yep, this might wind up a world of pain. I'm willing to give it a shot though, look to see if some more teeth into the types catches bugs that can patch back in as C fixes...if that works out, maybe push for more. – HostileFork says dont trust SE Dec 16 '12 at 22:45
  • 1
    Sure, that was argued. And it was true to the extent that you can take a C codebase, compile it as C++ after fixing some syntax things, it will *probably* do roughly the same afterwards as it did before so there won't be too many bugs to fix. Then it's no longer C and you can start incrementally using C++ features as the code evolves from that point. I don't think the "strength" you're talking about has anything to do with trying to maintain polyglot code. The exception is shared header files containing `extern "C"` interfaces, which are intended to be dual-language with use of `__cplusplus`. – Steve Jessop Dec 16 '12 at 22:47
  • 2
    Then, if you want a mixed code base of components written in C with others written in C++ then you can have that thanks to C linkage. Compile the components written in C using a C compiler, and the components written in C++ using a C++ compiler. Simples! :-) – Steve Jessop Dec 16 '12 at 22:50
  • 1
    Note that using `strchr` on `unsigned char *` arguments is invalid C too. C has no implicit conversion from `unsigned char *` to `char *`, **even if** "plain" `char` happens to be unsigned. GCC and some other compilers are just notoriously bad about accepting invalid C... – R.. GitHub STOP HELPING ICE Dec 16 '12 at 23:44

1 Answers1

2

Initial note: I am aware that this is also quite an ugly thing to do, but it might help you with a (hopefully) better idea!

How about using a macro:

#define strchr(s, c) ((unsigned char const *)strchr((char const *)s, c))

You could include it as a compile flag (-D) from you Makefile/build script. I suppose you have a custom Makefile/build script for your C++ project, so you do not need to add this macro to the C code-base.

To make it slightly better, you can use an additional file, that you include from your Makefile/build script, which adds these macros in a (more) structured way to CPPFLAGS

Veger
  • 37,240
  • 11
  • 105
  • 116