Getting multiple warnings.... ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

I am working on a project for my church to make multiple devices talk to each other via a 10 button keypad, this keypad will talk directly to the Arduino via API REST HTTP calls, and the Arduino will send the necessary commands out to the various devices to set the devices to the appropriate inputs. It is still early in the programming, as I am modifying a program that I created for a business venture. But it always had these errors in it, now I would like to figure out why they are there, and how to remove them. (Don't like having warnings in my code output, looks messy)

Here is the code:

#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <aREST.h>
#include <avr/wdt.h>

boolean powerStatus = false; // ADJUST remove this line eventually
boolean powerStatusChapel = false; // track power status of chapel projector
boolean powerStatusCHall = false; // track power status of cultural hall projector
String CurrentInput = ""; // Pulpit = Pulpit, Clerk = Stake Clerk desk, Camera = Chapel Camera, Rear = Rear Chapel HDMI
String Input = "Pulpit";        // Default system to switch to pulpit HDMI upon initial power up

int HTTP_PORT = 1880;    // Old code for calling API REST in NodeRed.... to be deleted.
String HTTP_METHOD = "GET";
char HOST_NAME[] = "172.16.30.250";
String PATH_NAME = "/api";
String queryString = "?power=1&override=0";

unsigned int gametimeleft = 0;
boolean countdownEnded = false;

// Create aREST instance
aREST rest = aREST();

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0xDE, 0xAD, 0xDE, 0xAD, 0x12, 0x34
};
IPAddress ip(192, 168, 108, 29);
IPAddress extron(192, 168, 108, 26);
IPAddress matrix(192, 168, 108, 30);

EthernetServer server(80);
EthernetClient client;
EthernetClient client2;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println(F("Ethernet shield was not found.  Sorry, can't run without hardware. :("));
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  while (Ethernet.linkStatus() == LinkOFF) {
    Serial.println(F("Ethernet cable is not connected."));
    delay(500);
  }

  // start the Ethernet connection:
  Serial.println(F("Attempting to obtain IP from DHCP, stand by..."));
  Ethernet.begin(mac);
  delay(1000);
  if (Ethernet.begin(mac) == 0) {
    // Failed to configure ethernet using DHCP, reverting to static IP
    Serial.println(F("Failed to obtain DHCP address, reverting to "));
    Serial.println(ip);
    Ethernet.begin(mac, ip);
  } else {
    // Obtained DHCP address
    Serial.println(F("Received IP: "));
    Serial.println(Ethernet.localIP());
  }

  rest.function("button1", pushButton1); // Chapel projector power on
  rest.function("button2", pushButton2); // Chapel projector power off
  rest.function("button3", pushButton3); // Pulpit HDMI active
  rest.function("button4", pushButton4); // Stake Clerk HDMI active
  rest.function("button5", pushButton5); // Chapel Camera active
  rest.function("button6", pushButton6); // Rear HDMI active
  rest.function("button7", pushButton7); // Cultural Hall projector power on
  rest.function("button8", pushButton8); // Cultural Hall projector power off
  rest.function("button9", pushButton9); //
  rest.function("button0", pushButton0); //

  rest.function("setgametime", setGameTime);
  rest.function("override", setOverride);
  rest.function("addgametime", addGameTime);
  //rest.function("powerstatus", setPowerStatus);
  rest.function("controlpower", setControlPower);
  rest.function("controlinput", setControlInput);
  rest.variable("timeleft", &gametimeleft);
  rest.set_id("001");
  rest.set_name("Lakewood_input_controller");

  Serial.println(F("Ready for REST API."));
}

void loop() {
  EthernetClient client = server.available();
  rest.handle(client);



  // Make changes to input (but only if Input and CurrentInput differ
  if (CurrentInput != Input) {
    CurrentInput = Input;
    Serial.print("Chapel System currently set to: ");
    Serial.println(Input);
    if (Input == "Pulpit") {
      Serial.println("Going to Pulpit HDMI source");
      client2.connect(matrix, 5000);
      client2.write("6x4.\r"); //ADJUST for appropriate commands!!
      client2.stop();

      client2.connect(extron, 23);
      client2.write("X5!\r");
      client2.stop();

    }
  } else if (Input == "Clerk") {
    Serial.println("Going to Stake Clerk HDMI source");
    client2.connect(matrix, 5000);
    client2.write("6x4.\r"); //ADJUST for appropriate commands!!
    client2.stop();

    client2.connect(extron, 23);
    client2.write("X6!\r");
    client2.stop();

  } else if (Input == "Camera") {
    Serial.println("Going to Chapel Camera source");
    client2.connect(matrix, 5000);
    client2.write("6x4.\r"); //ADJUST for appropriate commands!!
    client2.stop();

    client2.connect(extron, 23);
    client2.write("X4!\r");
    client2.stop();

  } else if (Input == "Rear") {
    Serial.println("Going to Rear Chapel HDMI source");
    client2.connect(matrix, 5000);
    client2.write("6x4.\r"); //ADJUST for appropriate commands!!
    client2.stop();

    client2.connect(extron, 23);
    client2.write("X7!\r");
    client2.stop();
  }


}


int pushButton1(String buttonCommand) {

  rest.set_name("chapelProjectorOn");
}
int pushButton2(String buttonCommand) {

  rest.set_name("chapelProjectorOff");
}
int pushButton3(String buttonCommand) {

  rest.set_name("chapelSourcePulpit");
}
int pushButton4(String buttonCommand) {

  rest.set_name("chapelSourceClerk");
}
int pushButton5(String buttonCommand) {

  rest.set_name("chapelSourceCamera");
}
int pushButton6(String buttonCommand) {

  rest.set_name("chapelSourceRear");
}
int pushButton7(String buttonCommand) {

  rest.set_name("notDeclared");
}
int pushButton8(String buttonCommand) {

  rest.set_name("notDeclared");
}
int pushButton9(String buttonCommand) {

  rest.set_name("notDeclared");
}
int pushButton0(String buttonCommand) {

  rest.set_name("notDeclared");
}

int setGameTime(String GameCounter) {
  rest.set_name("setGameTime");
  Serial.print("Game Time received: ");
  Serial.println(GameCounter);

  gametimeleft = 0;
  // removed gametimeleft logic, not used here. To be deleted soon.
  return gametimeleft;
}

int setOverride(String GameOverride) {
  rest.set_name("override");
  if (GameOverride.toInt() == 1) {
    //timeOverride = true;
    Serial.println(F("Game time override received and started."));

    String queryString = "?override=1";
    // Removed NodeRed connection to REST API command call
    return 1;
  } else {
    //timeOverride = false;
    Serial.println(F("Game time override canceled."));

    String queryString = "?override=0";
    // Removed NodeRed connection to REST API command call
  }
  return 0;
}

int addGameTime(String GameCounter) {
  rest.set_name("addGameTime");
  Serial.print("Game time add function triggered, game time amount to add: ");
  Serial.println(GameCounter);

  gametimeleft = 0;
  // removed gametimeleft logic, not used here. To be deleted soon.
  return gametimeleft;
}

int setPowerStatus(String PowerSetting) {
  rest.set_name("powerstatus");
  if (PowerSetting.toInt() == 0) {
    if (powerStatus == true) {
      gametimeleft = 0;
      powerStatus = false;
      Serial.println(F("Power off screen and projector command received."));
      client2.connect(matrix, 4352);
      client2.write("%1POWR 0\r");

      String queryString = "?power=0&override=0";
      // Removed NodeRed connection to REST API command call

      delay(10000);

      return 0;
    }
  } else {
    if (powerStatus == false) {
      powerStatus = true;
      Serial.println(F("Power on screen and projector command received."));
      client2.connect(matrix, 4352);
      client2.write("%1POWR 1\r");

      String queryString = "?power=1";
      // Removed NodeRed connection to REST API command call

      delay(10000);

    }
  }
  return 1;
}

int setControlPower(String PowerSetting) {
  client2.connect(matrix, 4352);
  if (PowerSetting == "off") {
    //telling projector to shut down
    client2.write("%1POWR 0\r");
  } else if (PowerSetting == "on") {
    //otherwise tell projector to turn on
    client2.write("%1POWR 1\r");
  }
  return 001;
}

int setControlInput(String InputName) {
  client2.connect(matrix, 4352);
  if (InputName == "hdmi1") {
    client2.write("%1INPT 32\r");
  } else if (InputName == "hdmi2") {
    client2.write("%1INPT 33\r");
  } else if (InputName == "comp1") {
    client2.write("%1INPT 11\r");
  } else if (InputName == "comp2") {
    client2.write("%1INPT 12\r");
  } else if (InputName == "avm-on") {
    client2.write("%1AVMT 31\r");
  } else if (InputName == "avm-off") {
    client2.write("%1AVMT 30\r");
  }
  return 11;
}


Check if there is any mechanism available to convert between String type and char*. String is a class while char* is a pointer to native type char, so direct conversion from String to char* might not be possible. Not sure if type String exposes any method to get its underlying buffer.


void StringToBstr(String inputStr, char **ouputStr)
{
    int strLen = len(inputStr); //or whatever way is there to get the length of type String

    (*outputStr) = new char[strLen + 1]; //1 more for ending null character

    memset(outputStr, strLen + 1, 0); //check syntax and arduino equivalent, or explicitly set each element to zero or NULL

    for(int i = 0; i < strLen; i++)
    {
        (*outputStr)[i] = (char)inputStr[i]; // or whatever way is to get each char out from String type
    }
}


/* ---- USAGE ---- */
char *output = NULL;

String inputStr = "hello";

StringToBstr(inputStr, &output);

print(output);

delete[] output; // clean up

Eventually a String can be cast into a constant char*?

String, I believe is not a native type so a direct cast to char* might not work unless there's some operator overloading used. Even so in the overload code we'd have to copy the characters to the char* output buffer similar to the code I proposed.

Another way could be,

String s = "hello";

const char* output = s.c_str(): //if supported

//OR

char *output = <static_cast>(char*)s;
//Or other cast variants such as <reinterpret_cast>, <dynamic_cast> and so on

don't know if you've set File->Preferences Compiler Warnings to "More"

since i don't have all the libraries, i tried synthesizing their symbols so that i could try to compile the code.

they were many errors i could fix and others i'm not sure are valid statements.

it seems you've written a lot (!) of code without making sure earlier (smaller) versions compile or even work

  • if a functions requires a char * arguments, a const char * can be cast as a (char*) to avoid the error. For example,
    rest.function((char*)"button1", pushButton1);,
    client2.write((char*)"%1POWR 0\r");, ...

  • i don't know if Serial knows how to print an "IPAddress" -- Serial.println(ip); and Serial.println(Ethernet.localIP());

  • what is the purpose of EthernetClient client = server.available();, to create a client or set it to a boolean?

  • it's not clear to me if your pushButton callback functions should be "void" or return a value. if they are declared an "int" they need to return a value

int pushButton1(String buttonCommand) {
    rest.set_name("chapelProjectorOn");
}

.c_str()

It does because the IPAddress class inherits from the Printable class. See IPAddress.h:

class IPAddress: public Printable {

Yes. I had mentioned it in my earlier post as well.

The warnings noted in the Subject of this thread:

Getting multiple warnings…. ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]

Area due to lines such as:

rest.function("button1", pushButton1);

Looking in aREST.h for the function() member of the aREST class you see:

void function(char * function_name, int (*f)(String)){

The compiler is unhappy with you passing a constant string literal to a function that expects a char *. The easiest workaround (hack) is to cast the literal to a (char *).

A better solution would be for the library's author to fix it with:

void function(const char * function_name, int (*f)(String)){

The ultimate solution is to modify the library function to take a const char*, provided the function does not alter the data pointed to by the char*. Note that some board packages have the compiler options set to flag this as an error instead of a warning, as an example the esp8266 boards.

Use rest.set_name((char*)"override"); instead of rest.set_name("override");

As long as you are getting a warning instead of an error there should not be a problem. All the warning messages are a nuisance, but the code should still compile correctly.

Today. The next iteration of the IDE they could be errors. I make it a point to fix all warnings for two reasons. 1. It extends the life of my code, and 2. It makes me a better programmer to understand what the warning is saying.

1 Like

Agreed. "0 errors, 0 warnings" is what I aim for too. Good programming practice!

Even if it's just a compiler warning, not an error, the question always arises for me: does the generated program code really do what I expect in every situation? or can it not happen that at some point, maybe when a certain value occurs, that the program gets out of hand?
That's why I always look very carefully at compiler warnings to see what the warning says. and I generally remove conversation warnings. I look at a warning about an unused variable, but especially during the development of a program, I leave such a variable in place if I still need it. However, before the final use of a program, I also clean up the code to that effect.

The problem is that truely fixing the problem involves fixing the library. Casting a const char* to char* is just masking over the warning, and also tells the compiler the function is allowed to alter the const data, which the function may do. You would need to look at the library code to know if a string literal really was an unacceptable argument to the function.

"fixing the library" is most annoying. We shouldn't have to fix someone else's library code. Suggesting that we post an issue on the Git sometimes gets a response, but mostly a waste of time when there are years' old issues still not addressed.

Unfortunately a lot of library code is no longer actively maintained, and some of these warnings were not flagged in older versions of the compiler. It has only been a couple of years since the warnings have shown up in the Arduino IDE, even when the preferences were set to show all warnings.

I particularly dislike actively maintained code that shows warning, in particular the Marlin firmware for 3D printers, which will generate pages of warnings about a compiler directive that may not be portable when all compiler warnings are shown.

While I appreciate the back and forth of there is something wrong with the library and library needs to be fixed. I am also of the mindset that warnings are tomorrow's errors.

That being said, how would I call this function:
rest.function("button1", pushButton1);
would that be with
rest.function((char*)"button1", pushButton1);

I was doing this coding while on the road this past week, a lot of the code that is present was functional in a previous code set I created for a business venture of mine (automating a TruGolf Multi-sport simulator), so while it may not look pretty (referring to the Ethernet class), it was functional at one point.

The aim of this program is for the Arduino to sit waiting for API REST commands from an external source, and then it would then send the commands out to the various objects to control them.

I'd think that cast should get rid of the warnings. Did it? Did the program work after?

As mentioned above, you should also check the library's source code to see if it makes any attempt to modify the string passed via the pointer. Modifying a literal constant could cause bad things to happen.

Well putting the (char*) in place did remove all warnings (had to shorten one of the set_names too). But I will need to run the program at my church (where the equipment is located at to see if it actually still works.