7
votes

I'm currently looking to calculate the total size of arguments passed into a function, in bytes. In theory, one can just write out sizeof(x) for every argument. However, this is a tremendous waste of time if one wants to do this for a lot of functions. I'm trying to figure out the amount of space for the arguments so I can allocate the right amount of memory to store them all and store them (for a variety of functions, with mixed types).

I'm looking to make an expression that can determine the size of all arguments to a non-variadic function, regardless of their names and regardless of how many there are (within reason, I'm fine with supporting only about 64 arguments for now). It can be a function, a preprocessor macro, I'm agnostic to the implementation. I'd also be interested in handling variadic functions, but I'm pretty sure that is impossible because by the time you're into a variadic function you've lost all information about the data types.

At present, I've found three approaches that might be twisted to let me do this. The first one is based on the concepts from Laurent Deniau's arg counting. In theory, I can use a macro to generate the function header, and do some similar fancy footwork to take the number of args and dispatch to various macros that handle EACH individual case where there are N arguments. (See: Ugly). Basically, I'd just be aliasing all the function names using a macro and then using sizeof on each one of them. The problem is, I'd need to make a macro for EVERY length of arguments I want to represent. And I'd really not prefer to make 64 (or more) things to do one job.

The second approach is to try to follow the approach of Ben Klemer's 'better variadic' stuff. I wouldn't be using all of his approach, but I'd be trying to generate a struct that represents the arg signature of a function into a structure. I could then try to get the sizeof the structure elements (or even the structure itself, if all I cared about was a conservative estimate of the space). This has a few issues. First, it may only work on C99 compliant things (still checking on that). Second, it leads to the creation an additional structure for every function implemented. That's not entirely a problem, but it still has the issue that his approach to making a struct ends up with the same names as the function (so you still need to refer to the names to use them). I could probably work around that though.

The third approach that would be possible would be a recursive macro, though I'm not sure how happy that makes compilers. It's theoretically possible to recursively pop elements off of VA_ARGS by calling a macro in the form POPPER(arg, ...) POPPER(VA_ARGS) + sizeof(arg). Clearly, it would need a halting rule for when VA_ARG is empty (and something to make sure you don't get caught with a floating + sign), but you get the idea.

Any one of these things would allow me to do this:

  1. Good, flexible ways to unpack the VA_ARGS from a variadic macro. If there's any way to index it
  2. A good example of a recursive macro that could be relied upon to do this (as well as its limitations in max # of args, compiler compatibility, standards compliance, etc).
  3. A way to directly get the total size of all the args through a different type of function inspection. GCC seems to have some crazy functions for constructing function calls for call forwarding that might be applicable, but these are compiler-specific, barely documented, and do not appear to report the size of the block of memory they allocate. They also report back tons of irrelevant information.
2
sizeof() never wastes any time since it gets always calculated by the compiler at compile time. It is not a function but merely a operator.x4u
I applaud your preparatory research, but do you have a specific, answerable question?Robert Harvey
It can be done in a more or less hackish and/or system/compiler dependent way but to give you a decent suggestion it would be nice if you could answer the question: What do you intend to do with this information and when do you need it? (at runtime, compile time or simply as a source code metric?)x4u
gobject-introspection and libffi are two libraries that may be related to what you're doing (though I'm not sure what you're doing)Havoc P
Well, this is related to a prior issue that I had trying to find a good C memoization library. Since nothing exists, I'm trying to do some up-front work that will save me time in adding/removing memoization to various functions. To do that, I need to be able to store a signature for the function inputs (either by defining and allocating an arg signature struct or pushing data into a malloc'ed block). Either way, I need a way to read an arbitrary number of args without knowing their names. Since programming is an information game, and the information exists, this should be possible.Namey

2 Answers

4
votes

You need a FOREACH macro that allows expanding another macro on every element of a variadic list. This works by defining variants for every list length of interest:

#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)
        :

Once you have that, you can write a macro to get the sizeof its arg and chain them together to add them:

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

So here is what I came up with in the end, with it being functional up to 64 args (or as many as you're willing to define the FOR and COUNT_ARGS functions, really). So, thanks for the help. Hopefully these tidbits are helpful to others- they represent my arrangement of some great ideas spread across the web.

I made the FOR_EACH construction a bit more general, so that I could use it for other stuff (e.g. multiplication, etc by just changing the prefixes and postfixes).


/* 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__)