Go Down

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

dougp

Dec 09, 2018, 04:42 am Last Edit: Dec 17, 2018, 07:31 pm by dougp
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?
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

#1
Dec 09, 2018, 03:00 pm Last Edit: Dec 09, 2018, 10:28 pm by J-M-L
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

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

@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
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

#3
Dec 09, 2018, 11:24 pm Last Edit: Dec 09, 2018, 11:29 pm by J-M-L
Post what you copied... feels weird (did you notice the template<typename T> before the function template)

Which compiler / IDE do you use?
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

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

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

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

J-M-L

I tried with 1.8.8 on a uno... May be your compiler version ?
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

J-M-L

#7
Dec 10, 2018, 12:01 am Last Edit: Dec 10, 2018, 12:12 am by J-M-L
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)
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

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.
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

I believe You can indeed have multiple versions of the IDE. A backup is always a good idea :)
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

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.


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

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.

dougp

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.
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
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));
}

dougp

#14
Dec 11, 2018, 04:47 pm Last Edit: Dec 11, 2018, 05:31 pm by dougp
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.
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