Arduino Forum

Using Arduino => Programming Questions => Topic started by: dougp on Dec 09, 2018, 04:42 am

Title: Creating a memcmp_E function [SOLVED]
Post by: dougp on Dec 09, 2018, 04:42 am
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
Code: [Select]

/*
   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
Code: [Select]

/*
   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
Code: [Select]

/*
   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?
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 09, 2018, 03:00 pm
would something like this help you get started?

Code: [Select]
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
Size of param = 1
Size of param = 2
Size of param = 4
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 09, 2018, 11:17 pm
@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:

Code: [Select]
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
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 09, 2018, 11:24 pm
Post what you copied... feels weird (did you notice the template<typename T> before the function template)

Which compiler / IDE do you use?
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 09, 2018, 11:39 pm
compiler: Arduino IDE 1.8.2

Code: [Select]


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() {}

Title: Re: Creating a memcmp_E function
Post by: gfvalvo on Dec 09, 2018, 11:57 pm
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.
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 10, 2018, 12:00 am
I tried with 1.8.8 on a uno... May be your compiler version ?
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 10, 2018, 12:01 am
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)
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 10, 2018, 12:25 am
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.
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 10, 2018, 01:10 am
I believe You can indeed have multiple versions of the IDE. A backup is always a good idea :)
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 10, 2018, 01:18 am
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 :).
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.


Title: Re: Creating a memcmp_E function
Post by: westfw on Dec 10, 2018, 10:44 am
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.
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 11, 2018, 04:55 am
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.

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
Code: [Select]

/*
   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

Code: [Select]

/*
   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
Code: [Select]

/*
   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.
Title: Re: Creating a memcmp_E function
Post by: westfw on Dec 11, 2018, 07:26 am
Quote
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:
Code: [Select]
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));
}
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 11, 2018, 04:47 pm
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.
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 11, 2018, 08:37 pm
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
Code: [Select]
#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
Code: [Select]

// -------------------------------------------------
// 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:
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

Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 12, 2018, 12:27 am
then run this code
This line:
Code: [Select]

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


returns with these errors -

Code: [Select]

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?
Title: Re: Creating a memcmp_E function
Post by: gfvalvo on Dec 12, 2018, 02:24 am
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:
Code: [Select]
#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:
Code: [Select]
#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.
Title: Re: Creating a memcmp_E function
Post by: gfvalvo on Dec 12, 2018, 03:12 am
Also, the code compiles fine without these two lines:
Code: [Select]
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.

Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 12, 2018, 03:28 am
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
Title: Re: Creating a memcmp_E function
Post by: J-M-L on Dec 12, 2018, 08:11 am
Hexspeak (https://en.m.wikipedia.org/wiki/Hexspeak) is fun :)

The code I posted compiled for me on a Mac with the latest Arduino IDE without warning and runs as posted

Weird
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 17, 2018, 01:49 am
Note that you're being non-standard;
Is this because of returning only true/false or something else?
Title: Re: Creating a memcmp_E function
Post by: westfw on Dec 17, 2018, 02:47 am
Quote
Quote
non-standard;
Is this because of returning only true/false
Yes.  Although, I think I thought you were going to return "true" for "match" (which would be opposite of the standard memcmp())
Title: Re: Creating a memcmp_E function
Post by: dougp on Dec 17, 2018, 04:42 am
Although, I think I thought you were going to return "true" for "match" (which would be opposite of the standard memcmp())
No, I did get that part right.  But, although my original thought was  to return only T/F, that's been amended.  It turns out the cost, if one looks at it that way, of comporting to memcmp() by returning a value is insignificant since memcmp() is doing the work anyway.  

I wrestled with the overloaded function approach but could not work out how to deal with endianness.  Doubtless ignorance on my part.

What appears below is essentially @J-M-L's template solution with a few changed names, another test or two,  and some added commentary.

.ino
Code: [Select]

// by J-M-L http://forum.arduino.cc/index.php?topic=584108.msg3978283#msg3978283

// version with print frills removed.
// -------------------------------------------------
// TEMPLATE DECLARATION
// -------------------------------------------------
#include <EEPROM.h>
#include "Header.h"

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

const uint32_t v1 = 0xDEADBEEF; // establish 32-bit values
const uint32_t v2 = 0xBADCAFFE;
const uint32_t v3 = 0xFEE4ABED;

// these strings start at eeprom addr 100
char ts1[] = "test str 1"; // each is eleven bytes total
char ts2[] = "test str 2";
char ts3[] = "test str 1";

uint8_t aByte = 0xEF;
uint8_t aBadByte = 0xFE;
uint8_t combo[] = {2, 2, 1, 0, 0}; // current lock combination in EEPROM

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(9600);
  //  Serial.println("Stored data used for compares:");
  //  Serial.println("const uint32_t v1 = 0xDEADBEEF @ EEPROM addr 8");
  //  Serial.println("const uint32_t v2 = 0xBADCAFFE @ EEPROM addr 12");
  //  Serial.println("const uint32_t v3 = 0xFEE4ABED @ EEPROM addr 16");
  //  Serial.println("aByteArray[] = {0xEF, 0xBE, 0xAD, 0xDE}\n");
  int EEPROM_addr = 8;
  if (!memcmp_E(&aByte, EEPROM_addr, 1)) Serial.println(F("test on aByte OK")); else Serial.println(F("test on aByte NOT OK"));
  Serial.println();

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

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

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

  if (!memcmp_E(aBadUnsignedLongArray, 8, nbItemsInaBadUnsignedLongArray)) Serial.println(F("test on aBadUnsignedLongArray OK")); else Serial.println(F("test on aBadUnsignedLongArray NOT OK"));
  //string compare
  if (!memcmp_E(ts2, 111, 5)) Serial.println(F("\ntest on string OK")); else Serial.println(F("\ntest on string NOT OK"));
  //  Serial.println();
  if (!memcmp_E(combo, 20, 5)) Serial.println(F("\ntest on string OK")); else Serial.println(F("\ntest on string NOT OK"));
}

void loop() {}



.h
Code: [Select]

// memcmp_E function
//
// by J-M-L http://forum.arduino.cc/index.php?topic=584108.msg3978283#msg3978283
// -------------------------
// Takes: a pointer to a RAM address, eeprom address, and length.
// Function compares the two pairs of addresses for
// len elements and returns true/false status of
// comparison.  Since memcmp() is used, if all elements match
// a zero (false) is returned, +- value otherwise.
// Compares char arrays, bytes, ints, doubles

template<typename T>
int memcmp_E(const T* ramStart, int eeprom_start, size_t  len )
{
  int result = 0; // create and initialize return value
  uint8_t sample[sizeof(T)]; // allocate a buffer for EEPROM.get
  for (size_t i = 0; i < len * sizeof(T); i += sizeof(T)) {
    EEPROM.get( eeprom_start + i, sample); // copy EEPROM data out to RAM
    // then use memcmp to do the compare - avoids dealing with endianness
    result = memcmp ( ((uint8_t *) ramStart) + i, (uint8_t *) sample, sizeof(T) );
    if (result) break; // ie. non-zero
  }
  return result;
}


the output
(http://forum.arduino.cc/index.php?action=dlattach;topic=584108.0;attach=286229)


Thanks to @J-M-L and @westfw for helping!