Skip to content

Instantly share code, notes, and snippets.

@xiam
Created September 10, 2013 11:45
Show Gist options
  • Save xiam/6508259 to your computer and use it in GitHub Desktop.
Save xiam/6508259 to your computer and use it in GitHub Desktop.
Un experimento para emular acceso a contexto desde métodos en C.
/*
* Éste experimento partió de una discusión iniciada por @toorandom y
* continuada por @nitr0usmx y @dexosexo:
*
* https://twitter.com/toorandom/status/377256908538671104
*
* El problema es obtener la dirección de una estructura a partir de uno de
* los apuntadores a funciones dentro de la misma. Como hacer un "this" o
* "self" dentro de un método en lenguajes oriendados a objetos.
*
* Según creo, esto no se puede hacer legalmente, y si no se puede hacer
* legalmente muy probablemente no sea portable, en vez de ésto, se propone
* usar el preprocesador de gcc como ayuda para tener uns sintaxis que haga
* sentido.
*
* $ gcc -o main main.c && ./main
* foo->b(1) = 42
* foo->c(1, 2) = 44
* bar->b(1) = 22
*
* */
#include <stdlib.h>
#include <stdio.h>
// Ésta es la parte del preprocesador que nos interesa:
// http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
#define METHOD(self, member, ...) self->member(self, __VA_ARGS__)
typedef struct temp {
int a;
// Tenemos pointer a funciones, estos pointer a funciones no pueden conocer
// el contexto "struct *temp" puesto que sólo son apuntadores, la única
// relación de estos apuntadores se puede definir en runtime.
int (*b)(struct temp*, int);
int (*c)(struct temp*, int, int);
} temp_t;
// Aquí van los métodos, de hecho me recuerda a la sintaxis de métodos de Go:
// https://gobyexample.com/methods
int B(temp_t *self, int x) {
return self->a + x;
}
// En Go sería: func (self *temp_t) C(x int, y int) int
int C(temp_t *self, int x, int y) {
return self->a + x + y;
}
int main() {
temp_t *foo;
foo = (struct temp*)malloc(sizeof(struct temp));
foo->a = 41;
foo->b = B;
foo->c = C;
// Básicamente B y C no pueden conocer (legalmente) foo por que no sabemos
// que dirección de memoria tendrá y por que viven en su propio planeta, B
// y C necesitan a "temp_t *" como primer argumento.
// @nitr0usmx comentaba que se puede saber la dirección en memoria de *foo
// y creo que es posible pero estaría difícil relacionar foo->b y foo->c
// (que apuntan a B y C) con otro apuntador *foo que no tenemos idea de dónde
// fue aventado. Ese tema no queda descartado, sería buena razón de otro
// experimento.
// Lo que tenemos que hacer es algo como foo->b(foo, 1), pero ésto sería
// repetir foo y pues qué feo repetir código, ¿no?
// Entonces, lo único que hago es ahorrar la chamba de repetir foo y pedirle
// al preprocesador que lo haga:
printf("foo->b(1) = %d\n", METHOD(foo, b, 1));
printf("foo->c(1, 2) = %d\n", METHOD(foo, c, 1, 2));
// Y para comprobar, veo el output del preprocessor:
// gcc -E main.c
// ...
// # 48 "main.c"
// printf("foo->b(1) = %d\n", foo->b(foo, 1));
//
// printf("foo->c(1, 2) = %d\n", foo->c(foo, 1, 2));
//
// Este método tiene la ventaja de que no necesita globales:
temp_t *bar;
bar = (struct temp*)malloc(sizeof(struct temp));
bar->a = 21;
bar->b = B;
printf("bar->b(1) = %d\n", METHOD(bar, b, 1));
return 1;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment