Conversión de una función miembro en una función estática en C ++

Estoy escribiendo un marco de prueba de memoria, en el que reemplazo las funciones de asignación de memoria dinámica con las mías (por ejemplo, malloc, realloc, free, etc.). Sin embargo, el sistema espera funciones estáticas (no puedo cambiar esto).

Tengo una clase MemoryTester, que registra las llamadas de memoria, y quiero vincular sus implementaciones de funciones miembro de las funciones de asignación de memoria. ¿Es esto posible con C ++?

EDIT:

Aquí hay un código que destaca lo que estoy tratando de hacer:

typedef void*(allocateFuncPtr) (uint8_t);
typedef void (freeFuncPtr) (void*);

void setAllocateFunction(allocateFuncPtr) {...}
void setFreeFunction(freeFuncPtr) {...}


class MemoryTester
{
    void *malloc(uint8_t size);
    void free(void *ptr);
}

int main()
{
    MemoryTester *tester = new MemoryTester();

    //want to set allocate and free functions to those in tester
    setAllocateFunction( tester->getAllocateFunction() );
    setFreeFunction( tester->getFreeFunction() );

    return 0;
}

preguntado el 16 de mayo de 11 a las 20:05

¿Tiene un objeto específico para llamar a estas funciones miembro en el momento de la vinculación? -

No puedo ver su código actual, ¿necesita punteros de función? si no, eche un vistazo a std :: bind. -

6 Respuestas

Después de la edición, la intención me parece más clara. Lo más simple que puede hacer es usar funciones miembro estáticas en el MemoryTester en lugar de funciones miembro no estáticas. Si necesitan mantener el estado, ese estado tendrá que moverse para ser static miembros de la clase.

contestado el 17 de mayo de 11 a las 01:05

Eso es lo que estoy haciendo ahora mismo, pero me preguntaba si habría una mejor manera. Supongo que C ++ no es flexible de esta manera. - Anne Nonimus

Esto no está relacionado con la flexibilidad, una función miembro no estática tiene una función oculta this parámetro, y eso hace que las firmas sean incompatibles. Si se le permitiera pasar el puntero a la función miembro, cuyo estado estaría modificando, qué objeto señalaría el implícito this ¿argumento? - David Rodríguez - dribeas

En otros lenguajes (JavaScript, por ejemplo), puede crear dinámicamente una función en tiempo de ejecución, por lo que puede crear un nuevo conjunto de funciones para cada objeto que inicialice. En otros idiomas, aún puede curvar el puntero del objeto en la función. Me preguntaba si C ++ tiene algo similar. - Anne Nonimus

El problema es que ha creado una interfaz fija, si en lugar de Requiriendo funciones sin argumentos en la interfaz que usó una opción más genérica que podría agregar el objeto a la llamada. Por ejemplo, si la función setAllocator almacenaba un std::function<void* (int)>, y la función en sí era una plantilla: template <typename F> void setAllocator( F allocator ); (o también usa std::function allí), entonces podrías usar std::bind para agregar el objeto a la llamada: setAllocator( &MemoryTester::allocate, &tester ) );. - David Rodríguez - dribeas

No estoy sugiriendo ese cambio en la interfaz, ya que eso implicaría un envío dinámico para cada llamada al asignador, y si realmente no lo necesita en producción, no tiene sentido modificar el diseño para la prueba. El problema aquí es que en C ++ tiene que comprometerse, pero en realidad puede decidir los compromisos. - David Rodríguez - dribeas

Si su compilador admite 0x lambdas, puede usar un cierre:

int main() {
  MemoryTester tester;  // If no need for dynamic allocation, don't use it.

  setAllocateFunction([&tester](uint8_t size) { return tester.malloc(size); });
  setFreeFunction([&tester](void *p) { tester.free(p); });

  return 0;
}

No se requieren cambios en su clase MemoryTester ni en la biblioteca a la que pertenezcan setAllocateFunction y setFreeFunction.


Sin lambdas, en lugar de una clase singleton, use un solo objeto global:

struct MemoryTester {
  void* malloc(uint8_t size);  // Neither are static.
  void free(void *p);
};

MemoryTester tester_a;
void* tester_a_malloc(uint8_t size) { return tester_a.malloc(size); }
void tester_a_free(void *p) { return tester_a.free(p); }

int main() {
  setAllocateFunction(&tester_a_malloc);
  setFreeFunction(&tester_a_free);
}

La ventaja aquí es la toda la clase MemoryTester no está paralizada debido a una necesidad particular para ello. Puede crear más funciones (por ejemplo, tester_b_malloc / free) según sea necesario para adaptarse a otros usos dentro del mismo programa.

contestado el 23 de mayo de 17 a las 14:05

Si está utilizando GCC, puede hacer que el enlazador ajuste la función por usted con la opción --wrap (o -Wl, - wrap, malloc).

Luego, puede interceptar llamadas a malloc porque todas las llamadas a esa función serán redirigidas a __wrap_malloc. Puede llamar a la función malloc real (c-lib original) usando el nombre __real_malloc o puede sustituir un reemplazo propio (y si no se llama a __real_malloc, entonces el enlazador puede eliminar el c- original) lib malloc todos juntos).

void *__wrap_malloc (size_t c)
{
  printf ("malloc called with %zu\n", c);
  return __real_malloc (c);
}

FWIW, puede reemplazar las operaciones globales nuevas y de eliminación y luego puede tener un programa que use solo su propio sistema de memoria (incluso cuando use código de bibliotecas compiladas de terceros, siempre y cuando no hayan sobrecargado las nuevas y eliminaciones globales).

Aquí tienes una gran información: Tutorial para reemplazar "nuevo" y "eliminar"

contestado el 17 de mayo de 11 a las 01:05

Esto no responde a la pregunta en absoluto, porque no son funciones miembro. - Cachorrito

La pregunta no está muy clara porque dice que quiere anular las funciones de memoria (malloc, et al). Estás diciendo que quiere hacerlo con funciones de miembro. No estoy muy seguro de dónde afirma que estas funciones deben ser "estáticas" funciona porque sus funciones miembro no están definidas como "estáticas". De todos modos ... supongo que es solo una mala elección de redacción donde ninguna de las respuestas será correcta porque la pregunta está mal definida. - Adisak

Dijo en su pregunta que no puede cambiar que su sistema tenga punteros de función libres. La pregunta está bien definida: tiene un marco que quiere punteros de función libres y quiere usar una función miembro. Cachorrito

No, en realidad dice y cito "el sistema espera funciones estáticas". Sin embargo, su ejemplo usa funciones miembro, que no son funciones estáticas ni funciones miembro estáticas. - Adisak

Respondí cómo reemplazar elementos cuando "el sistema espera funciones estáticas". La sustitución de las funciones miembro debe hacerse con virtuales o mediante una indirección de puntero (puntero sostenido por la clase pasada al probador de memoria). La única opción serían las variables miembro estáticas que contengan punteros de redirección si quiere que sean funciones miembro estáticas. Pero no está claro cuál de estos quiere. Aquí hay un par de miles de expertos en C ++ y su pregunta sería simple si estuviera bien definida, por lo que esperaría que alguien la respondiera correctamente si no estuviera tan claro. - Adisak

Si entiendo correctamente lo que pregunta, no hay ningún "compromiso" que hacer. Una vez que se ha asignado la memoria, puede decirle al compilador que es tipo por usuario un cast, en su caso un reinterpret_cast. Por ejemplo, en su función de asignación:

unsigned char* raw_memory = allocation( std::size_t );
MyFinalType* object = reinterpret_cast<MyFinalType*>(raw_memory); // tells the compiler to take this memory as a MyFinalType.

Luego devuelve su tipo final.

Para saber qué tipo usar, seguramente querrá usar plantillas.

No olvide que C ++ es un lenguaje de tipado estático: no puede vincular funciones a un tipo de objeto. Ok, puedes, pero tienes que usar std :: function o punteros de función, que no son lo que quieres aquí.

contestado el 17 de mayo de 11 a las 00:05

No a menos que hagas un objeto global. Si el marco toma punteros de función regulares, entonces está atascado con lo que proporciona, y si no proporciona el estilo tradicional void * userdata, entonces está lleno, o es hora de objeto global.

contestado el 17 de mayo de 11 a las 00:05

Tener un único objeto MemoryTester y tener funciones puente estáticas que llaman a funciones miembro de ese objeto.

contestado el 17 de mayo de 11 a las 00:05

Estoy rechazando esto. El OP podría apreciar el uso de un objeto global, pero no veo absolutamente ninguna necesidad de hacer que ese objeto sea único. - Cachorrito

Ahora que sé que singleton es una palabra prohibida para todos los escenarios posibles, desearía haber incorporado un goto en la respuesta también. - Dialéctico

Hay escenarios en los que un goto es aceptable (el código generado me viene a la mente como uno en el que la mayoría de la gente puede estar de acuerdo). No puedo decir lo mismo de un singleton. Así que sí, quizás deberías haber incorporado un goto - jalf

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.