Created
September 10, 2013 11:45
-
-
Save xiam/6508259 to your computer and use it in GitHub Desktop.
Un experimento para emular acceso a contexto desde métodos en C.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* É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