Wire.h two versions of same method, differ only in argument types WHY?

This was raised as part of a bug report for one of the cores I maintain, and I realized I don't understand exactly why this is done. The same idiom is repeated in many libraries around Arduino land.

Wire.h (excerpted)

class TwoWire : public Stream
{
  //snip
  public:
    //snip
    uint8_t requestFrom(uint8_t, uint8_t);
    //snip
    uint8_t requestFrom(int, int);
};

The corresponding implementations, from Wire.cpp:

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

uint8_t TwoWire::requestFrom(int address, int quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

What is the purpose of the requestFrom(int, int); ?

If that weren't there, and someone called requestFrom(a,b); where a and b are of type int (int16_t), wouldn't implicit type conversion convert those int16_t's to uint8_t's, and pipe that request to requestFrom(uint8_t, uint8_t)?

The actual background as to why it was brought to my attention, in the official "mega avr" board package, unlike classic AVR packages, they replaced

    uint8_t requestFrom(uint8_t, uint8_t);

with

    uint8_t requestFrom(uint8_t, size_t);

This causes certain code which compiles for classic AVRs to fail with errors like this:

C:\Users\lcagl\AppData\Local\Arduino15\packages\megaTinyCore\hardware\megaavr\2.0.2\libraries\Wire\src/Wire.h:75:13: note: candidate 1: uint8_t TwoWire::requestFrom(int, int)
uint8_t requestFrom(int, int);
^~~~~~~~~~~
C:\Users\lcagl\AppData\Local\Arduino15\packages\megaTinyCore\hardware\megaavr\2.0.2\libraries\Wire\src/Wire.h:73:13: note: candidate 2: uint8_t TwoWire::requestFrom(uint8_t, size_t)
uint8_t requestFrom(uint8_t, size_t);
^~~~~~~~~~~

Unfortunately the person who reported it gave me neither the complete error message nor the code that created it, but the difference between the signatures of the requestFrom() methods is apparent, and I assess that it's likely the cause of this issue... but naturally thinking about that led to my wondering exactly why there are multiple versions of requestFrom() that just typecast the arguments and call another version of requestFrom().

It allows people to use different types of variables and also allows a wider range of cross platform use. Your assertion that 'int' == 'int16_t' is not guaranteed across all platforms.

This sounds like it might be related to the changes made some time ago to stream based .write() commands which added integer overloads converted to uint8_t.

I think it all came about from issues around using the value of 0.

MegaAVR Wire.h: ArduinoCore-megaavr/Wire.h at master · arduino/ArduinoCore-megaavr · GitHub.
AVR Wire.h: ArduinoCore-avr/Wire.h at master · arduino/ArduinoCore-avr · GitHub.
SAMD Wire.h: ArduinoCore-samd/Wire.h at master · arduino/ArduinoCore-samd · GitHub.

Some use default 'int' for pin numbers and array sizes and I2C addresses and others like to squeeze every byte out of the code and use 'byte'.
The library meets both kind of people.

But that's not all :confused:

The 10-bit extended I2C address is not supported by the Wire library, but if it will be some day, then a uint8_t is not enough.
The SAMD version of the Wire library has 256 as buffer size since 2018. What if that is enlarged to 1024 for newer processors ? Then a 'int' or 'size_t' is required.

The MegaAVR version of the Wire library uses the 'size_t' as you mentioned and thus it is no longer compatible.
The MegaAVR should be compatible, but there are a few more things that they experiment with.
For example, the other libraries use 'TwoWire' and make it part of the 'Stream' class, but the MegaAVR uses 'TwoWire' part of the 'HardwareI2C' class.

Then there is a problem with 'virtual' to be able to use a software I2C library and still be able to use a pointer to the used library/object: Deriving from TwoWire pointless? · Issue #28 · Testato/SoftwareWire · GitHub.

Since Arduino IDE 1.8.13, there is a timeout for the I2C bus for the AVR Wire library. But I don't know if the other Wire libraries now also have a timeout.

Conclusion: It is not a bug, it is lack of consistancy.

But again - if at the time that the library (which is architecture specific) is authored, we know that the buffer will be smaller than 256 bytes, and 10-bit addresses will not be supported, why should it supply a version that expects ints, when we know that they will just be truncated to bytes?

What is it about this situation that makes it bad to not have the readFrom(int,int) version?

How is it fundamentally different from:

void setup() {
  Serial.begin(9600);
}

void test(uint8_t arg1) {
  Serial.println(arg1);
}

void test2(int arg1) {
  test((uint8_t)arg1);
}

void loop() {
  static int tempint=-50;
  test(tempint);
  test2(tempint);
  tempint+=25;
  delay(1000);
}

Not exactly the same, but when you get into larger types

void setup() {
  Serial.begin(9600);
}

void test(uint8_t arg1) {
  Serial.println(arg1);
}

void test(long arg1) {
  Serial.print( "Test with long:");
  test((uint8_t)arg1);
}

void loop() {
  static int tempint=-50;
  uint8_t temp8 = tempint;
  long tempL = tempint;
  test(tempint);
  test(temp8);
  test(tempL);
  tempint+=25;
  delay(1000);
}

you get

...
adaradio:19:15: error: call of overloaded 'test(int&)' is ambiguous

   test(tempint);

               ^

C:\Users\Brian\AppData\Local\Temp\arduino_modified_sketch_960433\adaradio.ino:6:6: note: candidate: void test(uint8_t)

 void test(uint8_t arg1) {

      ^~~~

C:\Users\Brian\AppData\Local\Temp\arduino_modified_sketch_960433\adaradio.ino:10:6: note: candidate: void test(long int)

 void test(long arg1) {

      ^~~~

exit status 1
call of overloaded 'test(int&)' is ambiguous
[code]
I don't know the history of the IDE, but there may have been issues in the past and if you cover all the bases, the code has a better chance or working and stay working as the IDE/compiler evolves

yes, it was me who forgot to send the code. I was once able to communicate with the fdc2214 (capacitive sensor reading chip) via i2c, but now it does not.

The code is as follows:

#include <Wire.h>
#include "FDC2214.h"
#include <EEPROM.h>

#define AVR_ATtiny1604

#define F_CPU 1000000L
//#define TWI_FREQ 100000L

const byte SDA_PIN = 6;
const byte SCL_PIN = 7;

//variables
float k;
float CRE;
float CRL;
float CLEV;
float CLEVbos;
float SMax;
float Max;

int HREF;
int i;
byte o; // sensor output type 0-4/20 ma.
String entry; // String that contain the entered character(s) from serial terminal keyboard
int sensorValue = 0; // the sensor value
int sonraki= 0;
int gosterge = 0;

#define CHAN_COUNT 3
float cap[CHAN_COUNT]; // variable to store data from FDC

FDC2214 capsense(FDC2214_I2C_ADDR_0); // Use FDC2214_I2C_ADDR_1

// ###
void setup() {
Wire.begin();
Wire.setClock(100000L);
delay(1000);
Serial.begin(57600);
Serial.println("\nFDC2214 Capacitive liquid level Sensor");
// ### Start I2C

pinMode (1,OUTPUT); // dac output
analogWrite(1,0);
//pinMode (3,OUTPUT); // fdc2214'ün sd pini
//digitalWrite(3, LOW); // FDC2214 Power ON

// ### Start FDC
// Start FDC2214 with 4 channels init
//bool capOk = capsense.begin(0x7, 0x5, 0x5, false); //setup three channels, autoscan with 3 channels, deglitch at 10MHz, external oscillator
// Start FDC2214 with 4 channels init
bool capOk = capsense.begin(0x7, 0x5, 0x5, true); //setup three channels, autoscan with 3 channels, deglitch at 10MHz, internal oscillator
if (capOk) Serial.println("Sensor OK");
else Serial.println("Sensor Fail");
}

FDC2214.h

// This is a header file for FDC2214 library
// By Harijs Zablockis, Intelitech, March 2018
// This file is heavily based on NelsonsLog_FDC2214.h by Chris Nelson
// Masks and channels added
//
// There is no warranty to the code. There is no support, don't ask for it.
// Use or skip on your own responsibility.
// NOT ALL FEATURES ARE TESTED!
//
// The code might get revisited at some point or it might not.
// The code does more than I need at the moment.
//
// Feel free to do whatever you want with it. No licensing BS. No limitations.
//

#ifndef FDC2214_H
#define FDC2214_H

#if ARDUINO >= 100
#include "Arduino.h"
#define WIRE_WRITE Wire.write
#else
#include "WProgram.h"
#define WIRE_WRITE Wire.send
#endif

#if defined(SAM3X8E)
typedef volatile RwReg PortReg;
typedef uint32_t PortMask;
#define HAVE_PORTREG
#elif defined(ARDUINO_ARCH_SAMD)
// not supported
#elif defined(ESP8266) || defined(ESP32) || defined(ARDUINO_STM32_FEATHER) || defined(arc)
typedef volatile uint32_t PortReg;
typedef uint32_t PortMask;
#elif defined(AVR)
typedef volatile uint8_t PortReg;
typedef uint8_t PortMask;
#define HAVE_PORTREG
#else
// chances are its 32 bit so assume that
typedef volatile uint32_t PortReg;
typedef uint32_t PortMask;
#endif

#define FDC2214_I2C_ADDR_0 0x2A
#define FDC2214_I2C_ADDR_1 0x2B
// Address is 0x2A (default) or 0x2B (if ADDR is high)

//bitmasks
#define FDC2214_CH0_UNREADCONV 0x0008 //denotes unread CH0 reading in STATUS register
#define FDC2214_CH1_UNREADCONV 0x0004 //denotes unread CH1 reading in STATUS register
#define FDC2214_CH2_UNREADCONV 0x0002 //denotes unread CH2 reading in STATUS register
#define FDC2214_CH3_UNREADCONV 0x0001 //denotes unread CH3 reading in STATUS register

//registers
#define FDC2214_DEVICE_ID 0x7F
#define FDC2214_MUX_CONFIG 0x1B
#define FDC2214_CONFIG 0x1A
#define FDC2214_RCOUNT_CH0 0x08
#define FDC2214_RCOUNT_CH1 0x09
#define FDC2214_RCOUNT_CH2 0x0A
#define FDC2214_RCOUNT_CH3 0x0B
#define FDC2214_OFFSET_CH0 0x0C
#define FDC2214_OFFSET_CH1 0x0D
#define FDC2214_OFFSET_CH2 0x0E
#define FDC2214_OFFSET_CH3 0x0F
#define FDC2214_SETTLECOUNT_CH0 0x10
#define FDC2214_SETTLECOUNT_CH1 0x11
#define FDC2214_SETTLECOUNT_CH2 0x12
#define FDC2214_SETTLECOUNT_CH3 0x13
#define FDC2214_CLOCK_DIVIDERS_CH0 0x14
#define FDC2214_CLOCK_DIVIDERS_CH1 0x15
#define FDC2214_CLOCK_DIVIDERS_CH2 0x16
#define FDC2214_CLOCK_DIVIDERS_CH3 0x17
#define FDC2214_STATUS 0x18
#define FDC2214_DATA_CH0_MSB 0x00
#define FDC2214_DATA_CH0_LSB 0x01
#define FDC2214_DATA_CH1_MSB 0x02
#define FDC2214_DATA_CH1_LSB 0x03
#define FDC2214_DATA_CH2_MSB 0x04
#define FDC2214_DATA_CH2_LSB 0x05
#define FDC2214_DATA_CH3_MSB 0x06
#define FDC2214_DATA_CH3_LSB 0x07
#define FDC2214_DRIVE_CH0 0x1E
#define FDC2214_DRIVE_CH1 0x1F
#define FDC2214_DRIVE_CH2 0x20
#define FDC2214_DRIVE_CH3 0x21

// mask for 28bit data to filter out flag bits
#define FDC2214_DATA_CHx_MASK_DATA 0x0FFF
#define FDC2214_DATA_CHx_MASK_ERRAW 0x1000
#define FDC2214_DATA_CHx_MASK_ERRWD 0x2000

class FDC2214 {
public:
FDC2214(uint8_t i2caddr);
//_i2caddr = i2caddr

boolean begin(uint8_t chanMask, uint8_t autoscanSeq, uint8_t deglitchValue, bool intOsc);

// double readCapacitance();
// To be used with FDC2112 and FDC2114
unsigned long getReading16(uint8_t channel);
// To be used with FDC2212 and FDC2214
unsigned long getReading28(uint8_t channel);

private:
void loadSettings(uint8_t chanMask, uint8_t autoscanSeq, uint8_t deglitchValue, bool intOsc);
// void setGain(void);
// double calculateCapacitance(long long fsensor);
// long long calculateFsensor(unsigned long reading);
void write8FDC(uint16_t address, uint8_t data);
void write16FDC(uint16_t address, uint16_t data);
uint32_t read32FDC(uint16_t address);
uint16_t read16FDC(uint16_t address);
uint8_t read8FDC(uint16_t address);
uint8_t _i2caddr;
};

#endif //include guard

and i found it...

https://forum.arduino.cc/index.php?topic=597595.0

but the problem persists...

megaTinyCore 2.0.4 will add a compatibility fix for this...

I return to the uint8_t versions of those calls (as this saves a bit of flash/ram), but have a requestFrom(uint8_t, size_t) and the three-argument version that casts-and-calls it the uint8_t version for compatibility with code that uses the "modern" calling conventions.