Go Down

Topic: Creating a memcmp_E function [SOLVED] (Read 629 times) previous topic - next topic

J-M-L

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

Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

dougp

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?
Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

gfvalvo

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.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

gfvalvo

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.

No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

dougp

#19
Dec 12, 2018, 03:28 am Last Edit: Dec 12, 2018, 04:04 am by dougp
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
Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

J-M-L

#20
Dec 12, 2018, 08:11 am Last Edit: Dec 12, 2018, 08:13 am by J-M-L
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
Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

dougp

Note that you're being non-standard;
Is this because of returning only true/false or something else?
Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

westfw

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

dougp

#23
Dec 17, 2018, 04:42 am Last Edit: Dec 17, 2018, 04:44 am by dougp
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



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

Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

Go Up