Differences with C++

Is there a document that lists the differences Arduino has with standard C++?

I get an error with class child : public base which is valid C++ code.
How does one specify inheritance in Arduino?

Hi @chris-80. Please post your full sketch. Please also post the full text of the error message you get when compiling the sketch.

It also makes a difference what the target MCU is. AVR boards use the avr-gcc compiler, The ESP32 family uses the xtensa-esp32-elf-gcc compiler. Similar, but not the same.

1 Like

It's also perfectly valid in Arduino. You're doing something wrong. But, unfortunately there's no way to tell what since you neglected to post complete code.

2 Likes

There are several "standards" as the language is evolving.

There exist documents that specify which standards the various Arduino C++ compilers strictly conform to.

Can you please point me to these documents.

One example would be the compiler used for AVR processors, which is GCC 7.3, part of the GNU Compiler Collection. Minimal web search skills will easily turn that up!

Other processors use later versions.

1 Like

As I understand it depending on the platform they use one of three compilers, GCC, Clang, MSVC. Over time the versions used have changed. The language is still evolving and there will be more changes as time passes. The best I can suggest is look at the compiler output and determine which one is being used and what version. You can then go on line and find the documentations for that particular version.

It is also important to note that he 'Arduino language' is C++, but it is very different from most C++ varieties. The 'Arduino language' has a lot of abstraction built in to it, especially involving the hardware interfaces. These abstractions have made it very simple to use. This was designed in Italy as a learning device for students. Here is a good read: Invention Story and History of Development of Arduino

I understand the Arduino language is C++ for the most part but not fully. It doesn't accept access modifiers such as protected or public in class inheritance, e.g. class A : public B ... for the Arduino IDE 2.3.4 targeting an UNO R3 board. If I compile the exact same sketch and targeting the same board from PlatformIO it's fine.

PlatformIO appears to be using the avr-g++ compiler, I have no idea what the Arduino IDE is using for the UNO boards or why it rejects perfectly good C++ syntax. Which brings me back to wanting to find a reference to the differences between Arduino's C++ and ISO standard C++.

That.

In general Arduinos are bare metal embedded systesm, which means that they have quite small amounts of RAM, and no operating system. This makes dynamic allocation (which is rampant in C++ libraries) dangerous, and probably interferes with exception handling.

In addition, avr-g++ for Uno/etc have no libc++ or STL, and so are very limited in what C++ features they support. (Also, they're stuck back at the C++11 of the standard, which apparently leaves out some features that experienced C++ programmers find useful.)

The ESP platforms are relatively RAM-rich (several hundred Kbytes!) and tend to have at least one OS running to handle their radio and Internet protocol processing, and probably have the best C++ support.

You still haven't posted any code. This compiled for me with "Arduino AVR Boards by Arduino", version 1.8.6. Works on Wokwi (don't have an Uno)

struct RGB : public Printable {
  byte R, G, B;
  RGB(byte R, byte G, byte B) : R(R), G(G), B(B) {}
  size_t printTo(Print& p) const override {
    size_t ret = 0;
    ret += p.print("R:");
    ret += p.print(R);
    ret += p.print(",G:");
    ret += p.print(G);
    ret += p.print(",B:");
    ret += p.print(B);
    return ret;    
  }
};

class Dimmer {
  RGB (&colors)[3];
public:
  Dimmer(RGB (&colors)[3]) : colors(colors) {}
  void task() {
    for (RGB &rgb : colors) {
      Serial.println(rgb);
    }
  }
};

void setup() {
  Serial.begin(115200);
  RGB data[] {
    {1, 1, 1}, 
    {2, 2, 2}, 
    {3, 3, 3},
  };
  Dimmer test {data};
  test.task();
}

void loop() {}

Yes, the code I'm working with is RTClib which is hundreds of lines long. I'll extract just the classes involved:

class RTC_I2C {
protected:
  /*!
      @brief  Convert a binary coded decimal value to binary. RTC stores
    time/date values as BCD.
      @param val BCD value
      @return Binary value
  */
  static uint8_t bcd2bin(uint8_t val) { return val - 6 * (val >> 4); }
  /*!
      @brief  Convert a binary value to BCD format for the RTC registers
      @param val Binary value
      @return BCD value
  */
  static uint8_t bin2bcd(uint8_t val) { return val + 6 * (val / 10); }
  Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
  uint8_t read_register(uint8_t reg);
  void write_register(uint8_t reg, uint8_t val);
};

/**************************************************************************/
/*!
    @brief  RTC based on the DS1307 chip connected via I2C and the Wire library
*/
/**************************************************************************/
class RTC_DS1307 : public RTC_I2C {
public:
  bool begin(TwoWire *wireInstance = &Wire);
  void adjust(const DateTime &dt);
  uint8_t isrunning(void);
  DateTime now();
  Ds1307SqwPinMode readSqwPinMode();
  void writeSqwPinMode(Ds1307SqwPinMode mode);
  uint8_t readnvram(uint8_t address);
  void readnvram(uint8_t *buf, uint8_t size, uint8_t address);
  void writenvram(uint8_t address, uint8_t data);
  void writenvram(uint8_t address, const uint8_t *buf, uint8_t size);
};

The Arduino compiler complains about 'public' expected a class.

Yes, things like non-trivial initialization aren't implemented.

I'm writing the code to support several boards for the Binary Clock Shield I got from NixieTesters.com. It only supported the UNO and I wanted to have it work on a Wemos D1 R32 ESP32 based UNO style board. I want to have it use the WiFi for syncing with a NTP server and create an app to change the colors of the LEDs and change the Alarm Melody. The D1 R32 UNO board has hardware issues, the pin defined for the Neopixel data output is an INPUT only pin (GPIO 34) so that required hardware changes to the board. I got a new ESP32-S3 based UNO board that works without hardware modifications.
The ESP32 uses RTOS as the base OS and with dual cores and lots of RAM it's like desktop development. The UNO R3 is another beast.

Instead of chasing down dependencies, made some fake ones.

struct Adafruit_I2CDevice {};
struct TwoWire {} Wire;
struct DateTime {};
struct Ds1307SqwPinMode {};

class RTC_I2C {
protected:
  /*!
      @brief  Convert a binary coded decimal value to binary. RTC stores
    time/date values as BCD.
      @param val BCD value
      @return Binary value
  */
  static uint8_t bcd2bin(uint8_t val) { return val - 6 * (val >> 4); }
  /*!
      @brief  Convert a binary value to BCD format for the RTC registers
      @param val Binary value
      @return BCD value
  */
  static uint8_t bin2bcd(uint8_t val) { return val + 6 * (val / 10); }
  Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
  uint8_t read_register(uint8_t reg);
  void write_register(uint8_t reg, uint8_t val);
};

/**************************************************************************/
/*!
    @brief  RTC based on the DS1307 chip connected via I2C and the Wire library
*/
/**************************************************************************/
class RTC_DS1307 : public RTC_I2C {
public:
  bool begin(TwoWire *wireInstance = &Wire);
  void adjust(const DateTime &dt);
  uint8_t isrunning(void);
  DateTime now();
  Ds1307SqwPinMode readSqwPinMode();
  void writeSqwPinMode(Ds1307SqwPinMode mode);
  uint8_t readnvram(uint8_t address);
  void readnvram(uint8_t *buf, uint8_t size, uint8_t address);
  void writenvram(uint8_t address, uint8_t data);
  void writenvram(uint8_t address, const uint8_t *buf, uint8_t size);
};

void setup() {}

void loop() {}

Added setup and loop as usual at the bottom. Compiles without complaint.

In the Settings, turn on "Show verbose output during compile"

Compiling sketch...
/Users/kenb4/Library/Arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -Wall -Wextra -std=gnu++11 ...

Scroll to the right and it says -std=gnu++11, as mentioned

A little "magic" is applied to an .ino file to convert it to the .ino.cpp that is compiled. There's a hidden #include <Arduino.h> inserted at the top of the file, which adds a bunch of stuff. Forward declarations are inserted so that the order of functions does not matter. As mentioned, some standard libraries are omitted on AVR. But it's not changing the language grammar.

The next step is to show all the errors. If you're familiar with C++, you know how problems can cascade. Please post them as code as well. If there's just that one error about "public" and you don't want to post all your code: after you verify that what I posted also works for you, work your way to the middle to see what triggers the problem.

2 Likes

Which error?

Thanks for your help.

I also got your code to compile, so that means I have an error up the chain somewhere. I'll start over with a fresh copy of RTClib and make the changes again.

The thing was it compiled from PlatformIO but when I tried it on the Android IDE it failed complaining at 'public' that it expected a class. I also noticed that if the include file name had to match the case as if I was on Linux not Windows. When I see errors such as non-trivial designated initializers not supported

In file included from src\BinaryClock.cpp:54:0:
src\BinaryClock.h:648:72: sorry, unimplemented: non-trivial designated initializers not supported
       AlarmTime alarm1 = { .number = ALARM_1, .melody = 0, .status = 0 };  // DS3232 alarm, includes seconds in alarm.

I know there are differences compared to the tools I'm used to. I'd just like a reference I can check to see what other differences exist for something like an UNO. The ESP32 boards don't have these same errors but they also use a different compiler.

Instead of hand waving and paraphrasing error messages, post a real code. Preferably an Minimal Reproducible Example (MRE). This is the smallest possible COMPLETE code that demonstrates the problem at hand. Leave out everything extraneous and unrelated.

Then, post the complete compiler error messages, not your interpretation of them.

PlatformIO and Arduino use essentially the same C++ compiler...
(by which I mean avr-gcc, arm-none-eabi-gcc, xtensa-*-elf-gcc, etc. There may be some version differences, and they may be set up for different C++ "standards", but you shouldn't see any major differences in stuff like basic syntax.)
(Looks like ST has the most 'cutting edge' compiler)

"Arduino 101" Arduino arc-elf32-g++ (ARCompact/ARCv2 ISA elf32 toolchain arc-2016.03-rc2-2-ga657822) 4.8.5                                                                           
rp2040: arm-none-eabi-g++ (GCC) 10.3.0                                                  
AVR DxCore:    avr-g++ (GCC) 7.3.0                                                             
STM;    arm-none-eabi-g++ (xPack GNU Arm Embedded GCC x86_64) 13.2.1 20231009           
ESP32-C*: riscv32-esp-elf-g++ (crosstool-NG esp-12.2.0_20230208) 12.2.0                   
ESP32:  xtensa-esp32s3-elf-g++ (crosstool-NG esp-2021r2-patch5) 8.4.0                   
ESP32s2: xtensa-esp32-elf-g++ (crosstool-NG esp-12.2.0_20230208) 12.2.0                  
esp32s3: xtensa-esp32s2-elf-g++ (crosstool-NG esp-12.2.0_20230208) 12.2.0                
?Again?: xtensa-esp32s3-elf-g++ (crosstool-NG esp-12.2.0_20230208) 12.2.0                
xtensa-esp32-elf-g++ (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0       
esp8266: xtensa-lx106-elf-g++ (GCC) 4.8.2                                                
?again?: xtensa-lx106-elf-g++ (crosstool-NG 1.20.0) 4.8.2                                
AVR "Arduino7": avr-g++ (GCC) 7.3.0                                                             
AVR "Arduino5": avr-g++ (GCC) 7.3.0                                                             
arm-none-eabi-g++ (GNU Tools for ARM Embedded Processors) 4.8.3 Arduino (Due?):20140228 (release) [ARM/embedded-4_8-branch revision 208322]                                   
Arduino (SAMD): arm-none-eabi-g++ (GNU Tools for Arm Embedded Processors 7-2017-q4-major) 7.2.1 20170904 (release) [ARM embedded-7-branch revision 255204]                     
Seeed SAMD: arm-none-eabi-g++ (GNU Tools for Arm Embedded Processors 7-2017-q4-major) 7.2.1 20170904 (release) [ARM embedded-7-branch revision 255204]                     
Adafruit SAMD: arm-none-eabi-g++ (GNU Tools for Arm Embedded Processors 9-2019-q4-major) 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599\

It will be interesting to see what the issue is; and whether it could have been expressed in a reference such that you would have avoided it.

We're all targeting different MCUs with different compilers. You can minimize additional differences by putting code in its own module in the usual way: a separate .cpp with a corresponding .h that you can #include in the .ino to use it.

You can create a New Tab for each .cpp and .h, which will live in the sketch directory alongside the .ino. You can also have a subdirectory named src, and subdirectories with any name under that. The arduino-cli backend will recurse that to find all the source files, compile them, and let the linker figure it out. If you're using src, then in the IDE Settings, turn on "Show files inside Sketches" so the Sketchbook shows the file tree. The IDE also has no mechanism to create files in subdirectories; but that's not much of a hurdle, and it will pick up new files that appear.

Earle Philhower has them beat with the "Raspberry Pi Pico/RP2040/RP2350" boards platform using GCC 14.3.0: