2011-06-02 17 views
7

Actualmente estoy buscando calcular el tamaño total de los argumentos pasados ​​a una función, en bytes. En teoría, uno solo puede escribir sizeof(x) para cada argumento. Sin embargo, esta es una gran pérdida de tiempo si uno quiere hacer esto para muchas funciones. Estoy tratando de calcular la cantidad de espacio para los argumentos, así puedo asignar la cantidad correcta de memoria para almacenarlos y almacenarlos (para una variedad de funciones, con tipos mixtos).C Función que calcula el tamaño total de los argumentos

Estoy buscando hacer una expresión que pueda determinar el tamaño de todos los argumentos para una función no variadica, independientemente de sus nombres e independientemente de cuántos hay (dentro de lo razonable, estoy de acuerdo con el apoyo solo sobre 64 argumentos por ahora). Puede ser una función, una macro de preprocesador, soy agnóstico para la implementación. También me interesaría manejar funciones variadas, pero estoy bastante seguro de que es imposible porque para cuando estás en una función variadica has perdido toda la información sobre los tipos de datos.

En este momento, he encontrado tres enfoques que podrían ser retorcidos para permitirme hacer esto. El primero se basa en los conceptos de Laurent Deniau's arg counting. En teoría, puedo usar una macro para generar el encabezado de la función, y hacer un trabajo de pies de fantasía similar para tomar el número de argumentos y despachar a varias macros que manejan CADA caso individual donde hay N argumentos. (Ver: feo). Básicamente, me limitaría a aliasar todos los nombres de funciones usando una macro y luego usando sizeof en cada uno de ellos. El problema es que necesitaría hacer una macro para TODOS los argumentos que quiero representar. Y realmente no preferiría hacer 64 (o más) cosas para hacer un trabajo.

El segundo enfoque es tratar de seguir el enfoque de Ben Klemer's 'better variadic' stuff. No usaría todo su enfoque, pero estaría intentando generar una estructura que represente la firma arg de una función en una estructura. Podría tratar de obtener el tamaño de los elementos de la estructura (o incluso la estructura en sí, si todo lo que me importaba fuera una estimación conservadora del espacio). Esto tiene algunos problemas. En primer lugar, es posible que solo funcione en cosas que cumplen con C99 (aún lo estoy comprobando). En segundo lugar, conduce a la creación de una estructura adicional para cada función implementada. Eso no es del todo un problema, pero todavía tiene el problema de que su enfoque para hacer una estructura termina con los mismos nombres que la función (por lo que todavía necesita consultar los nombres para usarlos). Sin embargo, probablemente podría evitarlo.

El tercer enfoque que sería posible sería un macro recursivo, aunque no estoy seguro de lo feliz que eso hace a los compiladores. Es teóricamente posible extraer elementos de forma recurrente de VA_ARGS llamando a una macro en el formato POPPER(arg, ...) POPPER(VA_ARGS) + sizeof(arg). Claramente, necesitaría una regla de detención para cuando VA_ARG esté vacío (y algo para asegurarse de no quedar atrapado con un signo flotante +), pero se entiende la idea.

Cualquiera de estas cosas me permitiría hacer esto:

  1. buenas maneras y flexibles para desempaquetar los VA_ARGS desde una macro variadic. Si hay alguna manera de indexarlo
  2. Un buen ejemplo de macro recursiva en la que se puede confiar para hacer esto (así como sus limitaciones en max # de argumentos, compatibilidad del compilador, cumplimiento de estándares, etc.).
  3. Una forma de obtener directamente el tamaño total de todos los argumentos a través de un tipo diferente de inspección de función. GCC parece tener algunas funciones crazy para construir llamadas a funciones para el reenvío de llamadas que podrían ser aplicables, pero estas son específicas del compilador, apenas documentadas y no parecen informar el tamaño del bloque de memoria que asignan. También informan toneladas de información irrelevante.
+2

'sizeof()' nunca pierde tiempo ya que siempre lo calcula el compilador en tiempo de compilación. No es una función sino simplemente un operador. – x4u

+3

Aplaudo su investigación preparatoria, pero ¿tiene una pregunta específica y responsable? –

+1

Se puede hacer de una manera más o menos hackish y/o dependiente del sistema/compilador, pero para darte una sugerencia decente sería bueno si pudieras responder a la pregunta: ¿Qué piensas hacer con esta información y cuándo lo haces? ¿necesito? (en tiempo de ejecución, tiempo de compilación o simplemente como una métrica de código fuente?) – x4u

Respuesta

3

Necesita una macro FOREACH que permita expandir otra macro en cada elemento de una lista variadic. Esto funciona mediante la definición de variantes para cada longitud de la lista de interés:

#define _NUM_ARGS(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...) N 
#define NUM_ARGS(...) _NUM_ARGS(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) 
#define EXPAND(X)    X 
#define FIRSTARG(X, ...)  (X) 
#define RESTARGS(X, ...)  (__VA_ARGS__) 
#define FOREACH(MACRO, LIST) FOREACH_(NUM_ARGS LIST, MACRO, LIST) 
#define FOREACH_(N, M, LIST) FOREACH__(N, M, LIST) 
#define FOREACH__(N, M, LIST) FOREACH_##N(M, LIST) 
#define FOREACH_1(M, LIST) M LIST 
#define FOREACH_2(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_1(M, RESTARGS LIST) 
#define FOREACH_3(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_2(M, RESTARGS LIST) 
     : 

Una vez que tienes eso, se puede escribir una macro para obtener el sizeof su arg y encadenarlos juntos para añadirlos:

#define SUM_SIZEOF(X) +sizeof(X) 
size_t size = FOREACH(SUM_SIZEOF, (int, int, double, float)); 
+0

Interesante y parece plausible para hacer cosas. Busqué en la documentación de Boost y parecía que tenían algunas formas de simplificar una macro de estilo ForEach (usando algunas funciones que operan en una secuencia). ¿Sería eso una forma más limpia de hacer algo similar? – Namey

+0

Eso, por supuesto, siempre que uno pueda usar Boost en línea C. Todavía no lo he probado, así que es sólo un pensamiento. – Namey

+0

Terminé haciendo algo muy similar a esto, de hecho, usando sus consejos y un enfoque similar aquí: http://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in- variadic-macros. Publicaré el código final como respuesta, aunque estoy aceptando tu respuesta. Gracias. – Namey

1

Así que esto es lo que se me ocurrió al final, con funcionalidades de hasta 64 argumentos (o tantos como esté dispuesto a definir las funciones FOR y COUNT_ARGS, realmente). Muchas gracias por la ayuda. Afortunadamente, estas cositas son útiles para los demás: representan mi disposición de algunas ideas geniales distribuidas por la web.

Hice la construcción FOR_EACH un poco más general, por lo que podría usarlo para otras cosas (por ejemplo, multiplicación, etc. simplemente cambiando los prefijos y postfijo).


/* CONCATENATE from Gregory Pakosz 
    Source: http://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros 
*/ 

#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) 
#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) 
#define CONCATENATE2(arg1, arg2) arg1##arg2 

/* --------------------------------- 
    | Variadic/Iteration Macros | 
    ---------------------------------*/ 

/***************************************************** 
COUNT_ARGUMENTS Counts the number of args to a variadic function, up to 64 (renamed from PP_NARG) 
Description: P_NARG macro returns the number of arguments that have been passed to it. 
Author: Laurent Deniau 
Source: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb?hl=en%29 
NOTE: This may not work reliably if the function receives zero args, depending on compiler 
*******************************************************/ 

#define COUNT_ARGUMENTS(...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) 
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__) 
#define PP_ARG_N(\ 
      _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
     _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ 
     _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ 
     _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ 
     _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ 
     _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ 
     _61,_62,_63,N,...) N 
#define PP_RSEQ_N() \ 
     63,62,61,60,     \ 
     59,58,57,56,55,54,53,52,51,50, \ 
     49,48,47,46,45,44,43,42,41,40, \ 
     39,38,37,36,35,34,33,32,31,30, \ 
     29,28,27,26,25,24,23,22,21,20, \ 
     19,18,17,16,15,14,13,12,11,10, \ 
     9,8,7,6,5,4,3,2,1,0 


/***************************************************** 
FOR_EACH_COMPOSER Composition macro to create expressions where some sequence is bound by prefix postfix 
Description: For each macro, but built more generally to allow expressing sums as well as series of functions. 
Adapted from: Gregory Pakosz 
Source: http://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros 
Functional up to 64 arguments. 
*******************************************************/ 

#define FOR_EACH_COMPOSER_1(prefix, postfix, finalPrefix, finalPostfix, x, ...) finalPrefix(x)finalPostfix 
#define FOR_EACH_COMPOSER_2(prefix, postfix, finalPrefix, finalPostfix, x, ...)\ 
    prefix(x)postfix\ 
    FOR_EACH_COMPOSER_1(prefix, postfix, finalPrefix, finalPostfix, __VA_ARGS__) 
#define FOR_EACH_COMPOSER_3(prefix, postfix, finalPrefix, finalPostfix, x, ...)\ 
    prefix(x)postfix\ 
    FOR_EACH_COMPOSER_2(prefix, postfix, finalPrefix, finalPostfix, __VA_ARGS__) 

/* Etc, up to 64 */ 

#define FOR_EACH_COMPOSER_(N, prefix, postfix, finalPrefix, finalPostfix, ...) CONCATENATE(FOR_EACH_COMPOSER_, N)(prefix, postfix, finalPrefix, finalPostfix, __VA_ARGS__) 

#define FOR_EACH_COMPOSER(prefix, postfix, finalPrefix, finalPostfix, ...) FOR_EACH_COMPOSER_(COUNT_ARGUMENTS(__VA_ARGS__), prefix, postfix, finalPrefix, finalPostfix, __VA_ARGS__) 

/***************************************************** 
SIZE_OF_ARGUMENTS Calculates the size of the given arguments 
Description: For each argument, calculates the sizeof returns the sum 
Author: Benjamin Nye 
NOTE: This may not work reliably if the function receives zero args, depending on compiler 
*******************************************************/ 
#define SIZE_OF_ARGS(...) FOR_EACH_COMPOSER(sizeof , +, sizeof , + 0, __VA_ARGS__) 
Cuestiones relacionadas