Creating a memcmp_E function [SOLVED]

As part of another project I needed to read/write EEPROM (UNO, 1K). Two of the functions used were memcmp() and EEPROM.get(). Rummaging through the AVR library functions I saw memcmp_P(), for comparing *n *bytes of RAM with flash memory but found no memcmp_E() counterpart dealing with EEPROM. Yes, there is EEPROM.read/write but I figured since these are likely built on the underlying avr/eeprom.h library they add abstraction overhead.

So, using Arduino IDE 1.8.2 and <avr/eeprom.h> I put together memcmp_E() which mimics memcmp_P() – having the form: memcmp_P(arg1(ram), arg2(flash), len) – except mine just returns true/false. Then I got the bright idea of extending the functionality to handle 16-bit arguments by adding to the function call argument list a number which is used to compute how many bytes are to be tested. I’d like to get rid of this and somehow have the compiler select a correct function for me.

What I have currently:

.ino

/*
   Attempt to duplicate memcmp_P but for EEPROM
   V1.6 accomodate 1-byte & 2-byte arguments and
   is converted to library format


   https://www.avrfreaks.net/forum/tut-c-using-eeprom-memory-avr-gcc
*/

#include "memcmp_E.h"

void setup() {

  Serial.begin(9600);
  // ram_data[] based on combo lock code starting at 0020h 12/6/18
  byte ram_data[] = {1, 7, 3, 1, 9, 5};
  // EEPROM data  =  1, 7, 3, 1, 9, 0
  int EEPROM_addr = 20;
  byte dLen = sizeof(ram_data) / sizeof(ram_data[0]);
  // compare bytes
  if ((memcmp_E(ram_data, EEPROM_addr, dLen, 1))) Serial.print("Data mismatch \n");
  else Serial.print("Data matches \n");
  //
  if ((memcmp_E(ram_data, EEPROM_addr, 5, 1))) Serial.print("Data mismatch \n");
  else Serial.print("Data matches \n");

  // compare words
  Serial.print("\n\n");
  if (memcmp_E(ram_data, EEPROM_addr, 2, 2)) Serial.println("word mismatch");
  else Serial.print("word match\n");
  if (memcmp_E(ram_data, EEPROM_addr, 3, 2)) Serial.println("word mismatch");
  else Serial.print("word match");

}

void loop() {}

.cpp

/*
   Operates like memcmp_P but works in the EEPROM
   space, with an additional argument to select
   size of the compared values.
   V1.6

   https://www.avrfreaks.net/forum/tut-c-using-eeprom-memory-avr-gcc
*/
#include "memcmp_E.h"
#include <avr/eeprom.h>

bool memcmp_E(uint8_t* ramAddr, uint16_t EE_addr, uint8_t len, uint8_t elementSize) {
  // Compares n bytes of EEPROM with RAM variable.
  // Caller provides:
  // RAM address, EEPROM address, number of elements, size (1 or 2 bytes)
  // of data to be compared.  Function exits on first mismatch.
  // Returns false if all bytes match, true if any mismatch detected.
  //
  if (elementSize > 2 or elementSize == 0)return;
  len *= elementSize; // set number of bytes to test
  for (int i = 0; i < len; i++) {
    uint8_t EE_data = eeprom_read_byte((uint8_t*)EE_addr++);
    if (*ramAddr++ - EE_data != 0)
      return true;
  }
  return false;
}

.h

/*
   Attempt to duplicate memcmp_P but for EEPROM
   V1.5 accomodate 1-byte & 2-byte arguments

   Next iteration: convert to void pointers and so
   eliminate the need for the size argument?

   https://www.avrfreaks.net/forum/tut-c-using-eeprom-memory-avr-gcc
*/
#include <avr/eeprom.h>

#ifndef memcmp_E_h
#define memcmp_E_h

class memcmp_E {

  public:

    memcmp_E(); //constructor    
    uint8_t* ramAddr;
    uint16_t EE_addr;
    uint8_t EE_data;
    uint8_t len;
    uint8_t elementSize;
    uint16_t i;
};
bool memcmp_E(uint8_t*, uint16_t, uint8_t, uint8_t);

#endif

The goal: I want to be able to compare *n *8-bit, 16-bit, and 32-bit arguments against like size arguments, with one universal function call and not have to specify argument size.

Questions I’ve asked on previous threads have raised templates and the use of ‘traits’ (an extension?) to achieve certain ends. Could these things be a part of a solution? I believe something like this is possible because Serial.print() does it.

Where should I start reading?

would something like this help you get started?

template<typename T>
void whatsMySize(T s)
{
  Serial.print(F("Size of param = "));
  Serial.println(sizeof(s));
}

template void whatsMySize<>(uint8_t); // instantiates whatsMySize<uint8_t>(uint8_t), template argument deduced
template void whatsMySize(int); 
template void whatsMySize<double>(double); // instantiates whatsMySize<double>(double)


void setup() {
  uint8_t b=1;
  int i=1;
  double d=1;

  Serial.begin(115200);
  whatsMySize(b);
  whatsMySize(i);
  whatsMySize(d);
}

void loop() {}

the console output running this on a UNO is

[color=purple]Size of param = 1
Size of param = 2
Size of param = 4[/color]

@J-M-L, thanks for the reply. I did a [Select]/copy/paste of your sketch but it does not compile for me, it errors out with this message:

sketch_dec09a:3: error: variable or field 'whatsMySize' declared void

 void whatsMySize(T s)

                  ^

sketch_dec09a:3: error: 'T' was not declared in this scope

exit status 1
variable or field 'whatsMySize' declared void

Post what you copied... feels weird (did you notice the template<typename T> before the function template)

Which compiler / IDE do you use?

compiler: Arduino IDE 1.8.2

template<typename T>
void whatsMySize(T s)
{
  Serial.print(F("Size of param = "));
  Serial.println(sizeof(s));
}

template void whatsMySize<>(uint8_t); // instantiates whatsMySize<uint8_t>(uint8_t), template argument deduced
template void whatsMySize(int); 
template void whatsMySize<double>(double); // instantiates whatsMySize<double>(double)


void setup() {
  uint8_t b=1;
  int i=1;
  double d=1;

  Serial.begin(115200);
  whatsMySize(b);
  whatsMySize(i);
  whatsMySize(d);
}

void loop() {}

Seems to me the easiest thing to do is just always tell the function how many bytes to compare. That way you don't need to mess with templates or function overloading. You can use the 'sizeof' directive to set the byte count in each call to the function.

I tried with 1.8.8 on a uno... May be your compiler version ?

gfvalvo:
Seems to me the easiest thing to do is just always tell the function how many bytes to compare. That way you don't need to mess with templates or function overloading. You can use the 'sizeof' directive to set the byte count in each call to the function.

Sure but it’s not fun :). (But would match what memcmp() does as it takes 2 pointers and the number of bytes to compare)

J-M-L:
I tried with 1.8.8 on a uno... May be your compiler version ?

Could 1.8.2 and 1.8.8 reside on the same machine without conflict?

I presume if I install 1.8.8 it will not destroy the existing sketch folder? Which I just backed up to external drive.

I believe You can indeed have multiple versions of the IDE. A backup is always a good idea :slight_smile:

gfvalvo:
Seems to me the easiest thing to do is just always tell the function how many bytes to compare. That way you don't need to mess with templates or function overloading. You can use the 'sizeof' directive to set the byte count in each call to the function.

J-M-L:
Sure but it’s not fun :).

What @J-M-L said! However, I am now considering a compromise - making the calling convention something like *memcmp_EB * and *memcmp_EW * for bytes and words respectively. This appeals to me because it would somewhat follow the AVR naming, à la pgm_read_byte , pgm_read_word , etc. making it more self-documenting and at the same time, as @gfvalvo states, "tell the function how many bytes to compare". It seems this might could simplify the under-the-hood stuff too.

I guess it'd be fun. You could use function overloading...

Note that you're being non-standard; memcmp() is a very old and very standardize function that is well defined to compare byte strings. There won't be any speed difference; both EEPROM and RAM are byte-wide and can only actually fetch one byte at a time.

westfw:
You could use function overloading...

I thought for overloading to work the argument lists must be distinguishable from one another. What I'm thinking is the same argument types (addresses & length) for any of the allowable types.

westfw:
Note that you're being non-standard; memcmp() is a very old and very standardize function that is well defined to compare byte strings. There won't be any speed difference; both EEPROM and RAM are byte-wide and can only actually fetch one byte at a time.

Yes, I understand. That's why the iterator in the loop which calls eeprom_read_byte must vary according to the caller's type.

My latest effort:

.ino

/*
   Attempt to duplicate memcmp_P but for EEPROM
   V1.6 accomodate 1-byte, 2-byte, and 4-byte arguments and
   is converted to library format


   https://www.avrfreaks.net/forum/tut-c-using-eeprom-memory-avr-gcc
*/

#include "memcmp_E.h"

void setup() {

  Serial.begin(9600);
  // ram_data[] based on combo lock code starting at 0020d 12/6/18
  byte ram_data[] = {1, 7, 3, 1, 9, 5};
  // EEPROM data  =  1, 7, 3, 1, 9, 0
  int EEPROM_addr = 20;
     
  // compare bytes
  if ((memcmp_E(ram_data, EEPROM_addr, 6))) Serial.print("Data mismatch \n");
  else Serial.print("Data matches \n");
  //
  if ((memcmp_E(ram_data, EEPROM_addr, 5))) Serial.print("Data mismatch \n");
  else Serial.print("Data matches \n");

  // compare words
  Serial.print("\n\n");
  if (memcmp_EW(ram_data, EEPROM_addr, 2)) Serial.println("word mismatch");
  else Serial.print("word match\n");
  if (memcmp_EW(ram_data, EEPROM_addr, 3)) Serial.println("word mismatch");
  else Serial.print("word match\n");

 // compare doubles
  Serial.print("\n\n");
  if (memcmp_ED(ram_data, EEPROM_addr, 1)) Serial.println("double mismatch");
  else Serial.print("double match\n");
  if (memcmp_ED(ram_data, EEPROM_addr, 2)) Serial.println("double mismatch");
  else Serial.print("double match\n");
}

void loop() {}

.cpp

/*
   Operates like memcmp_P but works in the EEPROM space.
   V1.6

   https://www.avrfreaks.net/forum/tut-c-using-eeprom-memory-avr-gcc
*/
#include "memcmp_E.h"
#include <avr/eeprom.h>

// Compares n bytes of EEPROM with RAM variable.
// Caller provides:
// RAM address, EEPROM address, number of elements to test
// Function exits on first mismatch.
// Returns false if all bytes match, true if any mismatch detected.
//
bool memcmp_E(uint8_t* ramAddr, uint16_t EE_addr, uint8_t len) {
  for (int i = 0; i < len; i++) {
    uint8_t EE_data = eeprom_read_byte((uint8_t*)EE_addr++);
    if (*ramAddr++ - EE_data != 0)
      return true;
  }
  return false;
};

bool memcmp_EW(uint8_t* ramAddr, uint16_t EE_addr, uint8_t len) {
  for (int i = 0; i < len*2; i++) {
    uint8_t EE_data = eeprom_read_byte((uint8_t*)EE_addr++);
    if (*ramAddr++ - EE_data != 0)
      return true;
  }
  return false;
};

bool memcmp_ED(uint8_t* ramAddr, uint16_t EE_addr, uint8_t len) {
  for (int i = 0; i < len*4; i++) {
    uint8_t EE_data = eeprom_read_byte((uint8_t*)EE_addr++);
    if (*ramAddr++ - EE_data != 0)
      return true;
  }
  return false;
};

.h

/*
   Attempt to duplicate memcmp_P but for EEPROM
     
   https://www.avrfreaks.net/forum/tut-c-using-eeprom-memory-avr-gcc
*/
#include <avr/eeprom.h>

#ifndef memcmp_E_h
#define memcmp_E_h

class memcmp_E {

  public:
    memcmp_E(); //constructor    
    uint8_t* ramAddr;
    uint16_t EE_addr;
    uint8_t EE_data;
    uint8_t len;
    uint8_t elementSize;
    uint16_t i;
};
bool memcmp_E(uint8_t*, uint16_t, uint8_t);

class memcmp_EW {

  public:
    memcmp_EW(); //constructor    
    uint8_t* ramAddr;
    uint16_t EE_addr;
    uint8_t EE_data;
    uint8_t len;
    uint8_t elementSize;
    uint16_t i;
};
bool memcmp_EW(uint8_t*, uint16_t, uint8_t);

class memcmp_EDW {

  public:
    memcmp_ED(); //constructor    
    uint8_t* ramAddr;
    uint16_t EE_addr;
    uint8_t EE_data;
    uint8_t len;
    uint8_t elementSize;
    uint16_t i;
};
bool memcmp_ED(uint8_t*, uint16_t, uint8_t);

#endif

No claim to elegance but it does work.

I thought for overloading to work the argument lists must be distinguishable from one another.

I'm pretty sure that an int* and char* are distinguishable.
This looks like it works:

int memcp(char*p1, char*p2, size_t l) {
    return memcmp(p1, p2, l);
}

int memcp(int*p1, int*p2, size_t l) {
    return  memcmp((char*)p1, (char*)p2, l*2);
}

char c1[] = "this is a test";
char c2[] = "another string";
int i1[5] = {1, 2, 3, 4, 5};
int i2[5] = {1, 2, 7, 6, 5};

int main() {
    printf("%d\n", memcp(c1, c2, sizeof(c1)));
    printf("%d\n", memcp(i1, i2, 5));
}

westfw:
This looks like it works:

There are gaps in my knowledge. How do I print the results on the serial monitor using printf()?

Never mind, I modified it to use Serial.print(). Testing.

This is a quick hack if what I had in mind with templated functions

start by running this once on a UNO to establish EEPROM values for addresses between 10 and 21

#include <EEPROM.h>

void setup() {
  const uint32_t v1 = 0xDEADBEEF;
  const uint32_t v2 = 0xBADCAFFE;
  const uint32_t v3 = 0xFEE4ABED;

  // do this once to establish EEPROM values
  EEPROM.put(10, v1);
  EEPROM.put(14, v2);
  EEPROM.put(18, v3);
}

void loop() {}

then run this code

// -------------------------------------------------
// TEMPLATE DECLARATION
// -------------------------------------------------
#include <EEPROM.h>

template<typename T>
int memcmp_E(const T* source, int eeprom_start, size_t  len )
{
  int result = 0;
  uint8_t sample[sizeof(T)];
  int eeAddress = eeprom_start;
  for (size_t i = 0; i < len * sizeof(T); i += sizeof(T)) {
    EEPROM.get( eeAddress + i, sample);

    Serial.print(F("Reading EEPROM @ address:"));  Serial.print((int) eeAddress + i); Serial.print(F("\t0x"));
    for (int j = sizeof(T) - 1; j >= 0 ; --j) {
      Serial.print(sample[j], HEX);
    }
    Serial.println();

    result = memcmp ( ((uint8_t *) source) + i, (uint8_t *) sample, sizeof(T) );
    if (result) break;
  }

  return result;
}

// teach the compiler about byte or unsigned long comparisons

template int memcmp_E<>(const uint8_t*, int, size_t);
template int memcmp_E<>(const uint32_t*, int, size_t);


// -------------------------------------------------


const uint32_t v1 = 0xDEADBEEF;
const uint32_t v2 = 0xBADCAFFE;
const uint32_t v3 = 0xFEE4ABED;

uint8_t aByte = 0xEF;
uint8_t aBadByte = 0xFE;

uint8_t aByteArray[] = {0xEF, 0xBE, 0xAD, 0xDE}; // because of LITTLE ENDIAN architecture
const size_t nbItemsInaByteArray = sizeof(aByteArray) / sizeof(aByteArray[0]);

uint32_t anUnsignedLongArray[] = {v1, v2, v3};
const size_t nbItemsInanUnsignedLongArray = sizeof(anUnsignedLongArray) / sizeof(anUnsignedLongArray[0]);

uint32_t aBadUnsignedLongArray[] = {v1, v3};
const size_t nbItemsInaBadUnsignedLongArray = sizeof(aBadUnsignedLongArray) / sizeof(aBadUnsignedLongArray[0]);



void setup() {
  Serial.begin(115200);
  if (!memcmp_E(&aByte, 10, 1)) Serial.println(F("test on aByte OK")); else Serial.println(F("test on aByte NOT OK"));
  Serial.println();

  if (!memcmp_E(&aBadByte, 10, 1)) Serial.println(F("test on aBadByte OK")); else Serial.println(F("test on aBadByte NOT OK"));
  Serial.println();

  if (!memcmp_E(aByteArray, 10, nbItemsInaByteArray)) Serial.println(F("test on aByteArray OK")); else Serial.println(F("test on aByteArray NOT OK"));
  Serial.println();

  if (!memcmp_E(anUnsignedLongArray, 10, nbItemsInanUnsignedLongArray)) Serial.println(F("test on anUnsignedLongArray OK")); else Serial.println(F("test on anUnsignedLongArray NOT OK"));
  Serial.println();

  if (!memcmp_E(aBadUnsignedLongArray, 10, nbItemsInaBadUnsignedLongArray)) Serial.println(F("test on aBadUnsignedLongArray OK")); else Serial.println(F("test on aBadUnsignedLongArray NOT OK"));

}

void loop() {}

You should see this in the Serial Console:

[color=purple]Reading EEPROM @ address:10	0xEF
test on aByte OK

Reading EEPROM @ address:10	0xEF
test on aBadByte NOT OK

Reading EEPROM @ address:10	0xEF
Reading EEPROM @ address:11	0xBE
Reading EEPROM @ address:12	0xAD
Reading EEPROM @ address:13	0xDE
test on aByteArray OK

Reading EEPROM @ address:10	0xDEADBEEF
Reading EEPROM @ address:14	0xBADCAFFE
Reading EEPROM @ address:18	0xFEE4ABED
test on anUnsignedLongArray OK

Reading EEPROM @ address:10	0xDEADBEEF
Reading EEPROM @ address:14	0xBADCAFFE
test on aBadUnsignedLongArray NOT OK
[/color]

J-M-L:
then run this code

This line:

int memcmp_E(const T* source, int eeprom_start, size_t  len )

returns with these errors -

jml_template_display:10: error: 'T' does not name a type

 int memcmp_E(const T* source, int eeprom_start, size_t  len )

                    ^

C:\Users\Owner\Documents\Arduino\a- memcmp_E collection\jml_template_display\jml_template_display.ino: In function 'int memcmp_E(const int*, int, size_t)':

jml_template_display:13: error: 'T' was not declared in this scope

   uint8_t sample[sizeof(T)];

                         ^

jml_template_display:16: error: 'sample' was not declared in this scope

     EEPROM.get( eeAddress + i, sample);

                                ^

Using library EEPROM at version 2.0 in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM 
exit status 1
'T' does not name a type

Needless to say, I don't know what to do about it. Could this be another manifestation of IDE version issues?

dougp:
Could this be another manifestation of IDE version issues?

Not version but core design philosophy. The IDE is messing things up by "helpfully" providing (incorrect) prototypes. The code compiles as posted with Eclipse / Sleober. Easiest thing to do if using Arduino IDE is to break the template definition into a .h file and then include that.

main.ino:

#include <EEPROM.h>
#include "Header.h"

const uint32_t v1 = 0xDEADBEEF;
const uint32_t v2 = 0xBADCAFFE;
const uint32_t v3 = 0xFEE4ABED;

uint8_t aByte = 0xEF;
uint8_t aBadByte = 0xFE;

uint8_t aByteArray[] = {0xEF, 0xBE, 0xAD, 0xDE}; // because of LITTLE ENDIAN architecture
const size_t nbItemsInaByteArray = sizeof(aByteArray) / sizeof(aByteArray[0]);

uint32_t anUnsignedLongArray[] = {v1, v2, v3};
const size_t nbItemsInanUnsignedLongArray = sizeof(anUnsignedLongArray) / sizeof(anUnsignedLongArray[0]);

uint32_t aBadUnsignedLongArray[] = {v1, v3};
const size_t nbItemsInaBadUnsignedLongArray = sizeof(aBadUnsignedLongArray) / sizeof(aBadUnsignedLongArray[0]);



void setup() {
  Serial.begin(115200);
  if (!memcmp_E(&aByte, 10, 1)) Serial.println(F("test on aByte OK")); else Serial.println(F("test on aByte NOT OK"));
  Serial.println();

  if (!memcmp_E(&aBadByte, 10, 1)) Serial.println(F("test on aBadByte OK")); else Serial.println(F("test on aBadByte NOT OK"));
  Serial.println();

  if (!memcmp_E(aByteArray, 10, nbItemsInaByteArray)) Serial.println(F("test on aByteArray OK")); else Serial.println(F("test on aByteArray NOT OK"));
  Serial.println();

  if (!memcmp_E(anUnsignedLongArray, 10, nbItemsInanUnsignedLongArray)) Serial.println(F("test on anUnsignedLongArray OK")); else Serial.println(F("test on anUnsignedLongArray NOT OK"));
  Serial.println();

  if (!memcmp_E(aBadUnsignedLongArray, 10, nbItemsInaBadUnsignedLongArray)) Serial.println(F("test on aBadUnsignedLongArray OK")); else Serial.println(F("test on aBadUnsignedLongArray NOT OK"));

}

void loop() {}

Header.h:

#ifndef HEADER_H
#define HEADER_H

template<typename T>
int memcmp_E(const T* source, int eeprom_start, size_t  len )
{
  int result = 0;
  uint8_t sample[sizeof(T)];
  int eeAddress = eeprom_start;
  for (size_t i = 0; i < len * sizeof(T); i += sizeof(T)) {
    EEPROM.get( eeAddress + i, sample);

    Serial.print(F("Reading EEPROM @ address:"));  Serial.print((int) eeAddress + i); Serial.print(F("\t0x"));
    for (int j = sizeof(T) - 1; j >= 0 ; --j) {
      Serial.print(sample[j], HEX);
    }
    Serial.println();

    result = memcmp ( ((uint8_t *) source) + i, (uint8_t *) sample, sizeof(T) );
    if (result) break;
  }

  return result;
}

// teach the compiler about byte or unsigned long comparisons

template int memcmp_E<>(const uint8_t*, int, size_t);
template int memcmp_E<>(const uint32_t*, int, size_t);

#endif

Note - this compiles, but I didn't test it.

Also, the code compiles fine without these two lines:

template int memcmp_E<>(const uint8_t*, int, size_t);
template int memcmp_E<>(const uint32_t*, int, size_t);

I'm guess the compiler can figure things out from the calling context without the specialization. Although, again, I only compiled. Didn't run it.

gfvalvo:
Not version but core design philosophy. The IDE is messing things up by "helpfully" providing (incorrect) prototypes. The code compiles as posted with Eclipse / Sleober. Easiest thing to do if using Arduino IDE is to break the template definition into a .h file and then include that.

Note - this compiles, but I didn't test it.

Thank you! Yes, moving the template as you describe cures it. Ran it and got identical results to your post above. Now, more perusing to understand what's going on.

Edit: I just noticed the cutesy letter sequences for the uint32 variables! ;D