2

I've noted that the following C code give "warning: initialization discards qualifiers from pointer target type" but it still compiles and behave as expected (outputting 'W' char).

#include <stdio.h>
int main(int argc, char *argv[])
{
    char buffer[20] = {'H','e','l','l','o',' ','W','o','r','l','d','!','\0'};

    const char* p = &buffer[0];

    char* c = (p + 6);

    printf("%c\n",*c);
}

In C++ rather similar code doesn't compile at all complaining about "error: invalid conversion from ‘const char*’ to ‘char*’ "

#include <iostream>
using namespace std;
int main()
{
   char buffer[20] = {'H','e','l','l','o',' ','W','o','r','l','d','!','\0'};

   const char* p = &buffer[0];

   char* c = p + 6;

   cout << *c;
   cout << endl;
   return 0;
}

What is the reason?

Is it possible to fix the C++ code to make it compile (and behave) like its C counterpart?

Better explanation: Thanks for all of your answers, but most of you didn't get my real problem so I'll try to explain in more details.

I'm using a library written in C. A function prototype in the header is something like:

void parse (const char* p, uint16_t len, uint8_t is_eof);

Inside the implementation of this function it happens to run code like

char* c = p + 6;

Everything is fine if I write my code in C and compile against this library.

Now I want to port this library in C++ since I need it in a C++ project. I successfully ported the library by redefining the function parameter as a variable rather than a constant. But since the p pointer actually need NOT to change I'd prefer to define it as a constant, as is in the original C lib. Putting a variable rather than a constant I end up porting a library without preserving all the original semantics. This is bad since I can't know if the C++ library runs as well as the original C one.

I hope this is clear enough.

A (hopefully) even better explanation:

I'm following the tutorial in AVR Tutorials - [CODE][C] Parsing strings flexibly/efficiently with Ragel and the issue is relative to the parse_microscript() function.

Since I'd like to embed Arduino code (that is, Serial.println();) in the parser, I'm trying to compile this Ragel parser in a C++ host language, rather than C as proposed in the tutorial.

First, I try to include Arduino.h in the generated C code, but the Arduino IDE won't compile (I believe because of mixing C and C++ code).

I then tried to generate C++ code from the same Ragel script, but when compiling multiple "invalid conversion from ‘const char*’ to ‘char*’" errors ar fired, targeting the generated code.

The only means to make it work in C++ had been to remove all constant keywords. This is bad since I'm changing the original semantic of the C code, but it works.

I tried to isolate the problem providing the above C and C++ snippets, but I probably poorly explained the point.

The accepted answer is what let the C++ snippet compile, but when I try this solution in Arduino code it doesn't work.

I put the working Ragel script compiled with the command ragel -G2 -o microscript.cpp microscript.rl:

#include "qp_port.h"
#include "pelican.h"
#include "bsp.h"
#include "Arduino.h"

static uint16_t currentNumber;

%%{
    machine microscript;

    action ClearNumber {
        currentNumber = 0;
    }

    action PrintNumber {
        Serial.print("parameter: ");
        Serial.println(currentNumber);
    }

    action RecordDigit {
        uint8_t digit = (*p) - '0';
        currentNumber = (currentNumber * 10) + digit;
    }

    number = ((digit @RecordDigit)+) > ClearNumber % PrintNumber;
    whitespace = space+;

    numlist = number (',' number)*;

    crlf = '\r\n';

    OK = crlf 'OK' crlf;

    main := |*

    crlf => {Serial.println("crlf");};

    OK => {Serial.println("OK detected");};

    *|;
}%%

%% Write data;

static uint8_t cs; /* The current parser state */
static char* ts;
static char* te;
static char* act;


void init_microscript() {
    %% write init;
}

void parse_microscript(char* p, uint16_t len, uint8_t is_eof) {
    char* pe = p + len; /* pe points to 1 byte beyond the end of this block of data */
    char* eof = is_eof ? pe : ((char*) 0); /* Indicates the end of all data, 0 if not in this block */

    %% write exec;
}

As you can see too, I had to change the first parameter of the parse_microscript function from const char* p to char*p , and const char* pe to char* pe.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Luky
  • 5,346
  • 4
  • 17
  • 15
  • 5
    `const` things should stay `const`. – chris Dec 07 '12 at 15:21
  • 4
    It is a second question in the last few days where I see people writing "Hello Word!". What is it about this word "world" that it is mistyped as "word" so often? It should be "Hello World!" – CygnusX1 Dec 07 '12 at 15:22
  • @CygnusX1 Ohhh, thanks I'm editing the answer. I hate mistyping too, sorry! – Luky Dec 07 '12 at 17:26
  • @chris I agree, I only need to understand why the author of the C implementation used const – Luky Dec 07 '12 at 17:34
  • Many Thanks @PeterMortensen. (And sorry for my english) – Luky Dec 09 '12 at 22:42

3 Answers3

9

You can fix both of them by using the correct types:

const char* c = p + 6;
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • Unfortunately I need p to be a const char* (to be passed to a library function) and i need to change the value of character c in the logic of my program – Luky Dec 07 '12 at 16:20
  • the first snippet need NOT to be fixed as it compiles and run as expected (did you read the question?) – Luky Dec 07 '12 at 16:22
  • @Luky Well, it helps if you ask the question you want answered! If you edit the question to include that information, I can change my answer appropriately. And yes, I read the question, and still think the first sample needs fixing in order to be portable (other compilers may not be as forgiving of this; they are allowed by the standard to refuse to compile such code). That warning isn't there for no reason. – R. Martinho Fernandes Dec 07 '12 at 16:30
  • To be fair I need to tell you the original C lib is specifically written for AVR architectures, hence compiled with avr-gcc which is not giving any warning (didn't tried all the compiler option though). The c++ port doesn't compile at all with the very same avr-gcc. – Luky Dec 07 '12 at 17:18
  • anyway "other compilers may not be as forgiving of this; they are allowed by the standard to refuse to compile such code" if it's true I think is the correct answer to my question. – Luky Dec 07 '12 at 17:20
5

That's why you should treat C and C++ as separate languages.

You can cast away the const :

char* c = (char*) p + 6;

But this is really a bad practice.

If you want to change a pointer why declare it as const?
If you want it to be a const, why do you want to cast away the constness?

Ask the above questions and decide it yourself.

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
P.P
  • 117,907
  • 20
  • 175
  • 238
  • 4
    const_cast, but this is a terrible idea and doesn't apply to the OP – Cat Plus Plus Dec 07 '12 at 15:29
  • 2
    This is **not** a difference between C and C++, just a difference in compilers. Both languages prohibit throwing away `const` without a cast. For violations of such rules, both languages require that the implementation **issue a diagnostic**. Both compilers did that. Having done so, the compiler is free to do pretty much anything. – Pete Becker Dec 07 '12 at 15:41
3

C++ is more strongly typed, C is not.

In C++ { 'H' ...}; is a array of constant characters (i.e. const char *). To initialize the variable buffer the type needs to have the const bit removed.

As C is not strongly typed it just issues a warning.

IMHO C++ is superior in this respect.

Ed Heal
  • 59,252
  • 17
  • 87
  • 127