Pregunta

¿Cómo puedo realizar la siguiente función genérica para uint8_t, uint16_t, uint32_t, int8_t, int16_t, int32_t y float_t?

No me gusta repetir la misma lógica que en cada caso como se puede ver.La única diferencia en cada caso es el casting.

Me gustaría idealmente como una solución que se adhiere al estándar de C y por lo tanto es portátil.Todas las ideas son bienvenidas.

Gracias.

static bool_t IsWithinLimits(const dbKey_t *key, const void *data)
{
    bool_t isWithinLimits = TRUE;
    limits_t limits = getDefinedLimits(key);

    switch(key->type)
    {
      case TYPE_UINT8:
        if((*(const UINT8*)data > (UINT8)limits.max) || (*(const UINT8*)data < (UINT8)limits.min))
        {
          isWithinLimits = FALSE;
        }
        break;

      case TYPE_UINT16:
        if((*(UINT16*)pData > (UINT16)limits.max) || (*(UINT16*)data < (UINT16)limits.min))
        {
          isWithinLimits = FALSE;
        }
        break;

      case TYPE_UINT32:
       ...
       break;

      case TYPE_INT8:
       ...
       break;

      case TYPE_INT16:
       ...
       break;

      case TYPE_INT32:
       ...
       break;

      case TYPE_FLOAT:
       ...
       break;
    }

  return isWithinLimits;
}
¿Fue útil?

Solución

Bien, usted podría extracto de los moldes:

int64_t loadptr_uint8(const void *p)  {
    return *(uint8_t*)p;
}
int64_t convert_uint8(int64_t val) {
    return (uint8_t)val;
}

int testLimits(const limits_t *plimits, const void *pData, int64_t(*loadptr)(void*), int64_t (*convert)(int64_t)) {
    return loadptr(pData) <= convert(limits->max) && loadptr(pData) >= convert(limits->min);
}

switch(key->type) {
    case TYPE_UINT8:
        isWithinLimits = testLimits(&limits, pData, loadptr_uint8, convert_uint8);
        break;
    // etc
}

O, si los distintos tipos de forma de un intervalo continuo de valores de 0, usted podría incluso hacer dos arrays de punteros a función y hacer:

bool isWithinLimits = testLimits(&limits, pData, loadptrs[key->type], converts[key->type]);

Notas:

  • Usted todavía tiene que escribir dos funciones para cada tipo, aunque son fácilmente macro-generado, si lo prefiere.
  • La verdad es que no parece que valió la pena para este pequeño código.
  • He elegido int64_t ya que es capaz de representar todos los valores de todos los tipos enteros de utilizar, por lo que las conversiones a int64_t nunca deseche la información y no cambiar nunca el resultado de una comparación con respecto a hacer la misma comparación en el tipo de fuente.Pero si también querían cubrir uint64_t, entonces usted no puede utilizar el mismo tipo para todo, ya que no hay ningún tipo entero que puede representar todos los valores de todos los tipos enteros.También le necesita separar testLimitsf función para float, quizás el uso de long double como el tipo más común de flexibilidad en el futuro.
  • [Editar:Me di cuenta de que, asumiendo la IEEE-754, double en realidad puede representar exactamente todos los valores de todos los tipos de uso.Así que con un leve portabilidad restricción, usted podría utilizar testLimitsf de todo y enfrentarse en dobles]
  • ¿Estás seguro de que la pena de conversión (por ejemplo) uint8_t antes de comparación?El valor está en el rango de una uint8_t, en cuyo caso no es necesario convertir, usted puede hacer la comparación.O bien el valor no está en el rango, en cuyo caso el modulo de reducción hace que la comparación un poco sin sentido, excepto en los casos especiales de 0 y -1.Por lo que podría ser la pena, si hay algo que usted no ha declarado que la hace tan, pero parece sospechoso para mí.
  • Usted dijo en un comentario, "estoy tratando de hacer que la función más eficiente".Esto podría ir en contra de eso.Es lógicamente posible en línea testLimits y también las llamadas a la fundición de funciones en el switch, pero no cuento con ello.

Otros consejos

No hay una manera fácil de hacer programación genérica como esta en C. Si está preocupado por el mantenimiento, esta puede ser una rara ocasión en la que una macro sea apropiada.

¿Por qué no utilizar una macro?

#define DO_MY_WORK(TYPE)\
  if((*(TYPE*)pData > (TYPE)tLimits.zMax) || (*(TYPE*)pData < (TYPE)tLimits.zMin))\
  {\
      isWithinLimits = FALSE;\
  }

La próxima revisión del estándar (C1x) agregará soporte para expresiones genéricas de tipo (ejemplo de wikipedia):

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

gcc tiene alguna compatibilidad preliminar con C1x.Creo que _Generic aún no es compatible, pero téngalo en cuenta para el futuro.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top