• Viernes 15 de Noviembre de 2024, 06:50

Autor Tema: [Artículo] Crear nuestra propia Run Time Library (Segunda Parte)  (Leído 7838 veces)

Eternal Idol

  • Moderador
  • ******
  • Mensajes: 4696
  • Nacionalidad: ar
    • Ver Perfil
[Artículo] Crear nuestra propia Run Time Library (Segunda Parte)
« en: Martes 22 de Junio de 2004, 09:29 »
0
Crear nuestra propia Run Time Library (Segunda Parte)

Primero que nada vamos a crear el directorio CLIB donde queramos y pasar los archivos que creamos en el primer tutorial ahi (C_ALLOC.CPP, C_MAIN.CPP, C_STR.CPP y C_WMAIN.CPP).

Yo uso una función con mucha frecuencia que simplemente muestra un número en pantalla con un MessageBox:
Código: C++
  1. void SayLong(int number)
  2. {
  3.   char Data[10];
  4.   ltoa(number,Data,10);
  5.   MessageBox(0,Data,"El Numero",0);
  6. }
  7.  

No tenemos acceso a ltoa al ser una función de la Run Time Library y por supuesto la vamos a implementar:
C_LTOA.CPP

Código: C++
  1. #include <string.h>
  2. extern "C" char *ltoa(long value,char * string,int radix)
  3. {
  4.   char *ptr;
  5.   unsigned digval;
  6.   ptr = string;
  7.   do
  8.   {
  9.     digval = (unsigned)(value % radix);
  10.     value /= radix;
  11.     *ptr++ = (char)(digval + 48);
  12.   } while (value > 0);
  13.   *ptr-- = '';
  14.   strrev(string);
  15.   return string;
  16. }
  17.  

C_STRREV.CPP
Código: C++
  1. extern "C" char *strrev (char *string)
  2. {
  3.   char *start = string;
  4.   char *left = string;
  5.   char ch;
  6.  
  7.   while (*string++);
  8.   string -= 2;
  9.  
  10.   while (left < string)
  11.   {
  12.     ch = *left;
  13.     *left++ = *string;
  14.     *string-- = ch;
  15.   }
  16.   return(start);
  17. }
  18.  

Tanto ltoa como strrev son funciones muy básicas que trabajan con cadenas de caracteres, vamos a crear nuestra libreria de esta forma:
cl /c *.cpp
lib *.obj /out:clib.lib


En el final de la primera parte les dije que ibamos a ver como crear los operadores new y delete, bueno vamos a intentar crear este más que simple programa y de paso probar atol y strrev:

SIMPLE.CPP (lo creamos en un directorio diferente al de la libreria)
Código: C++
  1. #include <windows.h>
  2. void SayLong(int number)
  3. {
  4.   char Data[10];
  5.   ltoa(number,Data,10);
  6.   MessageBox(0,Data,"El Numero",0);
  7. }
  8.  
  9. class Man
  10. {
  11.   public:
  12.   Man()
  13.   {
  14.     SayLong(66);
  15.   }
  16.   ~Man()
  17.   {
  18.     SayLong(99);
  19.   }
  20. };
  21.  
  22. int main()
  23. {
  24.   Man *yo = new Man;
  25.   return 0;
  26. }
  27.  

Con nuestra Run Time Library:
cl /c simple.cpp
link simple.obj /nodefaultlib clibclib.lib user32.lib kernel32.lib


Como algunos ya se habrán imaginado esto no es posible, todavía, porque no hemos creado el operador new.
Entonces el linker se queja:
simple.obj : error LNK2001: unresolved external symbol "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z)

Que es realmente lo que realiza el operador new? Es fundamental saber esto antes de intentar emular su funcionamiento; pese a los mitos que dominan el mundo de la informática el operador new no hace practicamente nada, el mismo se limita a llamar a malloc y devolver el puntero asignado por esta función.
Eso es todo lo que realiza new, pero no hay que pasar por alto algo muy importante, el compilador llama al constructor del objeto si usamos new. Por eso siempre que usemos objetos dinámicos debemos usar new por lógica.

Entonces vamos a crear el C_NEW.CPP teniendo en cuenta lo que ya sabemos:
Código: C++
  1. #include <stdlib.h>
  2. #include <malloc.h>
  3. void *operator new(unsigned int cb) { return malloc(cb); }
  4.  

En este código simplemente llamamos a malloc (nuestro malloc, el que creamos en C_ALLOC.CPP).

Recompilamos nuestra libreria:
cl /c *.cpp
lib *.obj /out:clib.lib


Volvemos a linkear (no hace falta volver a compilar) el programa anterior con nuestra Run Time Library así:
cl /c simple.cpp
link simple.obj /nodefaultlib clib.lib user32.lib kernel32.lib


Al ejecutar el programa simple.exe vemos el número 66 en pantalla, eso quiere decir que nuestro constructor fue llamado y como sabemos que nuestro código de new es una linea y no llama a ningún constructor tenemos la prueba fehaciente de que los constructores son llamados por el compilador y no por new.

Con este código estamos dejando memoria sin liberar, el objeto creado no está siendo destruido en ninguna parte, así que vamos a deshacer el error:

SIMPLE.CPP
Código: C++
  1. #include <windows.h>
  2. void SayLong(int number)
  3. {
  4.   char Data[10];
  5.   ltoa(number,Data,10);
  6.   MessageBox(0,Data,"El Numero",0);
  7. }
  8.  
  9. class Man
  10. {
  11.   public:
  12.   Man()
  13.   {
  14.     SayLong(66);
  15.   }
  16.   ~Man()
  17.   {
  18.     SayLong(99);
  19.   }
  20. };
  21.  
  22. int main()
  23. {
  24.   Man *yo = new Man;
  25.   delete yo;
  26.   return 0;
  27. }
  28.  

Intentamos compilarlo con nuestra linea de comandos de siempre
cl /c simple.cpp
link simple.obj /nodefaultlib clib.lib user32.lib kernel32.lib


Y nos encontramos con el siguiente error:
simple.obj : error LNK2001: unresolved external symbol "void __cdecl operator delete(void *)" (??3@YAXPAX@Z)

Si van siguiendo atentamente este tutorial ya deben saber que delete es simplemente una llamada a free y el compilador implementa la llamada al destructor, vamos a comprobarlo implementando nuestra versión del operador delete:

C_DELETE.CPP
Código: C++
  1. #include <stdlib.h>
  2. #include <malloc.h>
  3. void operator delete(void *p) { free(p); }
  4.  

Creamos la libreria con la linea de comandos pertinente y volvemos a linkear (no hace falta volver a compilar) el programa con nuestra linea de comandos de siempre, como esperabamos el resultado obtenido es un mensaje con el número 66 y otro con el 99, demostrando nuevamente como los constructores y destructores son llamados por el compilador.

Vamos a incluir un poco más de complejidad en nuestra clase para probarla:

SIMPLE.CPP
Código: C++
  1. #include <windows.h>
  2. void SayLong(int number)
  3. {
  4.   char Data[10];
  5.   ltoa(number,Data,10);
  6.   MessageBox(0,Data,"El Numero",0);
  7. }
  8.  
  9. class Man
  10. {
  11.   public:
  12.  
  13.   int edad;
  14.   int pais;
  15. };
  16.  
  17. int main()
  18. {
  19.   Man *yo = new Man;
  20.   Man *john = new Man;
  21.   Man *pedro = new Man;
  22.  
  23.   yo->edad = 20;
  24.   yo->pais = 54; //argentina
  25.   john->edad = 25;
  26.   john->pais = 1; //usa
  27.   pedro->edad = 14;
  28.   pedro->pais = 34; //españa
  29.  
  30.  
  31.   SayLong(yo->edad);
  32.   SayLong(john->edad);
  33.   SayLong(pedro->edad);
  34.  
  35.   delete yo;
  36.   delete john;
  37.   delete pedro;
  38.   return 0;
  39. }
  40.  

Volvemos a compilar, linkear y comprobar que el resultado es el esperado, los numeros que salen en pantalla son 20, 25 y 14, en ese mismo orden.

Generalmente las aplicaciones de consola usan parametros introducidos por el usuario en la linea de comandos, es una convención que el primer parametro sea el nombre de la aplicación.
Como podemos emular el funcionamiento de VC++ que no sólo nos pasa como parametros a nuestra función main, argc y argv sino que también los mantiene como variables estaticas?

De esta forma, vamos a reemplazar nuestro C_MAIN.CPP que no hacía nada por este otro que hace bastantes cosas:

Código: C++
  1. #include <malloc.h>
  2. #include <string.h>
  3.  
  4. extern "C" int __argc = 1;
  5. extern "C" char** __argv = 0;
  6.  
  7. extern "C" void __stdcall ExitProcess(unsigned long uExitCode);
  8. extern "C" char* __stdcall GetCommandLineA(void);
  9. extern int main(int argc, char *argv[],char *env[]);
  10.  
  11. extern "C" void mainCRTStartup()
  12. {
  13.   char *parametros = GetCommandLineA();
  14.   char *temp = (char*)malloc(2048);
  15.   memset(temp,0,2048);
  16.   __argc = 0;
  17.   __argv = (char**)malloc(4);
  18.   char Except = false;
  19.   while(*parametros)
  20.   {
  21.     if (*parametros == 34)
  22.     {
  23.       if (Except == false)
  24.       {
  25.         Except = true;
  26.       }
  27.       else
  28.       {
  29.         Except = false;
  30.         if (strlen(temp) > 0)
  31.         {
  32.           __argv = (char**)realloc(__argv,4 * (__argc+1));
  33.           __argv[__argc] = (char*)malloc(strlen(temp) + 1);
  34.           memset(__argv[__argc],0,strlen(temp) + 1);
  35.           strcpy(__argv[__argc],temp);
  36.           strcpy(temp,"");
  37.           __argc++;
  38.         }
  39.       }
  40.       parametros++;
  41.       continue;
  42.     }
  43.    
  44.     if ( (*parametros == 32) && (Except == false) )
  45.     {
  46.       if (strlen(temp) > 0)
  47.       {
  48.         __argv = (char**)realloc(__argv,4 * (__argc+1));
  49.         __argv[__argc] = (char*)malloc(strlen(temp) + 1);
  50.         memset(__argv[__argc],0,strlen(temp) + 1);
  51.         strcpy(__argv[__argc],temp);
  52.         strcpy(temp,"");
  53.         __argc++;
  54.       }
  55.     }
  56.     else
  57.     {
  58.       unsigned long pos = strlen(temp);
  59.       temp[pos] = *parametros;
  60.       temp[pos+1] = 0;
  61.     }
  62.     parametros++;
  63.   }
  64.   if (strlen(temp) > 0)
  65.   {
  66.     __argv = (char**)realloc(__argv,4 * (__argc+1));
  67.     __argv[__argc] = (char*)malloc(strlen(temp) + 1);
  68.     memset(__argv[__argc],0,strlen(temp) + 1);
  69.     strcpy(__argv[__argc],temp);
  70.     strcpy(temp,"");
  71.     __argc++;
  72.   }
  73.   free(temp);
  74.   main(__argc,__argv,0);
  75.   for (int y = 0;y < __argc;y++) { free(__argv[y]); }
  76.   free(__argv);
  77.   ExitProcess(0);
  78. }
  79.  

Si intentaramos construir la libreria en este momento no tendríamos ningún problema, pero cada vez que quisieramos utilizarla nos pediría las funciones _realloc y _memset por lo tanto sería una libreria completamente inútil.
realloc lo usamos porque no sabemos cuantos elementos va a tener la linea de comandos y memset para dejar un buffer lleno de ceros y no tener datos 'basura'.

C_REALLOC.CPP
Código: C++
  1. #include <windows.h>
  2. void *realloc(void *block, size_t size) { return HeapReAlloc(GetProcessHeap(),NULL,block,size); }
  3.  

C_MEMSET.CPP
Código: C++
  1. extern "C" void *memset(void *s,int c,size_t n)
  2. {
  3.   char *t = (char*)s;
  4.   while(n--)
  5.   {
  6.     *t++ = c;
  7.   }
  8.   return s;
  9. }
  10.  

Ahora si podemos crear la libreria con los parametros de siempre.

Este código consigue pasarle como parametros argc y argv a la función main utilizando la función GetCommandLine de la API de Windows, usa nuestra implementación de memoria dinámica para guardar los parametros y los libera cuando termina la función main (que es llamada con main(__argc,__argv,0);) finalmente llama a la función ExitProcess de la API de Windows.
De esta forma teniendo __argc y __argv emulamos perfectamente el funcionamiento de VC++, vamos a comprobarlo con este pequeño código:

CMDLINE.CPP
Código: C++
  1. #include <windows.h>
  2.  
  3. extern int __argc;
  4. extern char **__argv;
  5.  
  6. void SayLong(int number)
  7. {
  8.   char Data[10];
  9.   ltoa(number,Data,10);
  10.   MessageBox(0,Data,"El Numero",0);
  11. }
  12.  
  13.  
  14. void main(int argc,char *argv[])
  15. {
  16.   SayLong(argc);
  17.   for (int g = 0;g < argc;g++)
  18.   {
  19.     MessageBox(0,argv[g],argv[g],0);
  20.   }
  21.  
  22.   SayLong(__argc);
  23.   for (int h = 0;h < __argc;h++)
  24.   {
  25.     MessageBox(0,__argv[h],__argv[h],0);
  26.   }
  27. }
  28.  

Compilamos y linkeamos con:
cl /c cmdline.cpp
link cmdline.obj /nodefaultlib clib.lib user32.lib kernel32.lib


El programa funciona perfectamente, argc y __argc coinciden y argv y __argv también, prueben a compilarlo sin nuestra Run Time Library y obtendran los mismos resultados en cuanto a funcionamiento.

Bueno gente, eso es todo por ahora, en la tercera parte vamos a ver como implementar constructores y destructores para objetos estáticos.

Saludos,
Mariano.

Autor: Mariano Ventaja

http://www.c0d3rz.com.ar

Descarga: http://www.c0d3rz.com.ar/foro/viewtopic.php?t=94

Nacional y Popular En mi país la bandera de Eva es inmortal.


Queremos una Argentina socialmente justa, económicamente libre y  políticamente soberana.
¡Perón cumple, Evita dignifica!


La mano invisible del mercado me robo la billetera.

Ruben3d

  • Miembro HIPER activo
  • ****
  • Mensajes: 710
  • Nacionalidad: es
    • Ver Perfil
    • Web personal
Muy bien!
« Respuesta #1 en: Martes 22 de Junio de 2004, 13:56 »
0
El artículo está muy interesante, a la vez que instructivo. Espero la siguiente parte.

Ruben3d

Eternal Idol

  • Moderador
  • ******
  • Mensajes: 4696
  • Nacionalidad: ar
    • Ver Perfil
Gracias!
« Respuesta #2 en: Martes 22 de Junio de 2004, 13:59 »
0
Me alegro de que te haya gustado, ya tengo el código de la próxima parte funcionando a la perfección, voy a tratar de tener la explicación lo más pronto posible.

Saludos,
Mariano.

Nacional y Popular En mi país la bandera de Eva es inmortal.


Queremos una Argentina socialmente justa, económicamente libre y  políticamente soberana.
¡Perón cumple, Evita dignifica!


La mano invisible del mercado me robo la billetera.

mega18

  • Nuevo Miembro
  • *
  • Mensajes: 1
    • Ver Perfil
PREGUNTA
« Respuesta #3 en: Domingo 10 de Octubre de 2004, 02:34 »
0
QUIERO VER SI ME PUEDEN MANDAR UN EJEMPLO DE UN PREGRAMA EL CUAL ESTE N C++ Y QUE SEA UN COMPLILADOR DE UN PROGRAN HECHO EN OTRO ARCHIVO DE C++

Anónimo

  • Visitante
mi opinion:
« Respuesta #4 en: Viernes 8 de Abril de 2005, 02:37 »
0
me parece bien pero me gustaria que tuviera mucho mas conceptos acerca de las funciones de manejo de c++

Eternal Idol

  • Moderador
  • ******
  • Mensajes: 4696
  • Nacionalidad: ar
    • Ver Perfil
Re: mi opinión
« Respuesta #5 en: Viernes 8 de Abril de 2005, 20:02 »
0
No hay practicamente funciones de C++ en la CRT, se usan las mismas bibliotecas que en C mas Standard Template Library (STL).

Nacional y Popular En mi país la bandera de Eva es inmortal.


Queremos una Argentina socialmente justa, económicamente libre y  políticamente soberana.
¡Perón cumple, Evita dignifica!


La mano invisible del mercado me robo la billetera.