¿Está usando memcmp al actualizar la estructura en EEPROM?

3

Estoy escribiendo una función de actualización genérica para EEPROM que compara el valor antiguo con el nuevo y luego, si los valores difieren, escribe uno nuevo en eeprom. Esta es la función:

template <typename T>
void update(uint16_t update_address, T& update_value)
{
    // Do not attempt to read until device is ready
    while (!TM_I2C_IsDeviceConnected(EEPROM_I2Cx_, EEPROM_ADDRESS)) {}
    T current_value;
    read(update_address, current_value);
    if (memcmp(&current_value, &update_value, sizeof(T)) != 0)
    {
        // Do not attempt to write until device is ready
        while (!TM_I2C_IsDeviceConnected(EEPROM_I2Cx_, EEPROM_ADDRESS)) {}
        write(update_address, update_value);
    }
}

El problema es la comparación. Leí que, debido al relleno, las variables pueden diferir si se trata de estructuras en ciertos casos y generalmente es una mala idea usar memcmp para comparar dos estructuras. Pero quiero que mi función sea genérica. Podría asumir que el operador "==" está definido y usar

template <typename T>
void update(uint16_t update_address, T& update_value)
{
    // Do not attempt to read until device is ready
    while (!TM_I2C_IsDeviceConnected(EEPROM_I2Cx_, EEPROM_ADDRESS)) {}
    T current_value;
    read(update_address, current_value);
    if (current_value != update_value)
    {
        // Do not attempt to write until device is ready
        while (!TM_I2C_IsDeviceConnected(EEPROM_I2Cx_, EEPROM_ADDRESS)) {}
        write(update_address, update_value);
    }
}

pero me pregunto si en este caso se necesita REALMENTE un operador "==". Después de todo, simplemente estoy comparando bytes sin procesar, sin lógica involucrada (es decir, uint8_t boolean puede ser cierto en ambas estructuras, incluso si una es 1 y otra 100 y esto debería manejarse adecuadamente, pero en este caso no importa en absoluto como debería ser una y la misma estructura por byte), pero esa cosa de relleno me ha echado un poco. Por lo tanto, mi pregunta es: ¿Está bien en este caso en particular usar memcmp (T puede ser de cualquier tipo, probablemente en muchos casos una estructura) y el relleno puede causar problemas o debo asumir o, más bien, exigir explícitamente al operador "==" que ¿Ser definido antes de usar la función?

    
pregunta Terraviper-5

3 respuestas

3

Si realmente quieres que tu clase de plantilla sea genérica (y parece que quieres), deberías usar el operador ==. De hecho, podría haber bytes de relleno sin inicializar en la estructura, y su comparación con memcmp devolvería el valor falso incluso si todos los campos tienen el mismo valor. Por supuesto, esto requiere que el operador == esté definido adecuadamente para el tipo subyacente, pero es una suposición más segura que considerar que todos los bytes de la estructura tienen importancia.

Esta elección tiene las siguientes consecuencias:

  • Más flexibilidad: en algunos tipos subyacentes, es posible que tenga campos que no necesita / desea comparar. Es el caso si tiene campos que se deducen de otras partes de la estructura: por ejemplo, si el objeto contiene un ID y un nombre y sabe que hay una asociación de uno a uno entre nombre e ID, simplemente Hay que comparar la ID en el operador ==, haciendo que la comparación sea más rápida. Otro ejemplo: si, en la estructura, hay un hash en los datos del objeto, primero comparará el hash para verificar si los objetos son desiguales para mejorar la velocidad, antes de comparar todos los campos uno por uno.
  • La llamada del operador == puede eventualmente ser incorporada por el compilador, mientras que la llamada memcmp ciertamente no lo hará (a menos que se declare como inlineable, pero lo dudo). Esto puede permitir al compilador producir un código más eficiente. Por otro lado, esto también puede llevar a un código inflado. Verifique el código compilado en curso si tiene restricciones severas en el tamaño del código. Hay formas para evitar que el código se llene de plantillas, pero está fuera del alcance de su pregunta.
respondido por el dim
2
  

Leí que debido al relleno, las variables pueden diferir si se trata de estructuras en ciertos casos y, en general, es una mala idea usar memcmp para comparar dos estructuras.

De hecho. Esta es la razón por la que no debes simplemente descargar ciegamente una estructura en eeprom en primer lugar. No solo se arriesga a escribir bytes de relleno de basura en el eeprom, sino que también ocupan espacio innecesario. Esta es la raíz de tu problema.

Hay dos formas posibles de solucionar esto:

  • O se asegura de que sus estructuras no tengan relleno, si eso es posible para el sistema dado. Puede asegurarse de esto con un static_assert comparando el tamaño de la estructura con el tamaño de todos los miembros sumados.
    Para eliminar el relleno, deberás recurrir a un comando específico del compilador como #pragma pack .
  • O bien, escribe rutinas de serialización / des-serialización que copian miembros individuales uno por uno. Más lento pero mucho más portátil y robusto.

El mismo problema existe cuando desea enviar estructuras a través de protocolos de datos, por lo que esto no es nada exclusivo del caso EEPROM.

Una vez que se haya asegurado de conocer la naturaleza de los datos almacenados, puede compararlos con memcmp . El uso de un operador personalizado == no tiene mucho sentido, ya que de todos modos quiere una comparación binaria sin procesar, esto parece ser un controlador EEPROM.

Es una muy buena idea comparar los datos que se deben escribir con la EEPROM antes de escribirlos, ya que esto ahorrará ciclos de escritura y, por lo tanto, aumentará la vida de la memoria EEPROM.

Tenga en cuenta que no tiene mucho sentido escribir un controlador eeprom que funcione en otros tipos que no sean uint8_t . El conductor no tiene que saber la naturaleza de los datos.

    
respondido por el Lundin
1

No tiene sentido verificar si la actualización es necesaria en este nivel. La escritura de EEPROM es una operación extremadamente lenta. Esta comprobación se debe realizar en la función de escritura, por bytes o por página,

Un conjunto útil de funciones y macros incluiría el conocimiento de la organización de página interna de varias EEPROM I²C y permitiría a las personas crear fácilmente estructuras que se rellenan con esos límites de página, en lugar de los límites de página impuestos por el compilador.

    
respondido por el Janka

Lea otras preguntas en las etiquetas