计算参数总大小的 C 函数

发布于 2024-11-11 23:03:37 字数 1674 浏览 4 评论 0原文

我目前正在计算传递给函数的参数的总大小(以字节为单位)。理论上,我们可以为每个参数写出sizeof(x)。然而,如果想要对很多函数执行此操作,这将是一种极大的时间浪费。我试图计算出参数的空间量,以便我可以分配适量的内存来存储它们并存储它们(对于各种具有混合类型的函数)。

我正在寻找一个表达式,可以确定非可变参数函数的所有参数的大小,无论它们的名称如何,也无论有多少个参数(在合理范围内,我现在只支持大约 64 个参数) )。它可以是一个函数、一个预处理器宏,我对实现不可知。我也对处理可变参数函数感兴趣,但我很确定这是不可能的,因为当您进入可变参数函数时,您已经丢失了有关数据类型的所有信息。

目前,我发现了三种可能可以让我做到这一点的方法。第一个基于 Laurent 的概念Deniau 的 arg 计数。理论上,我可以使用宏来生成函数头,并执行一些类似的花哨步骤来获取参数数量并分派到处理有 N 个参数的每种情况的各种宏。 (参见:丑陋)。基本上,我只是使用宏为所有函数名称添加别名,然后对每个函数名称使用 sizeof 。问题是,我需要为我想要表示的每个长度的参数创建一个宏。我真的不喜欢做 64 件(或更多)东西来完成一份工作。

第二种方法是尝试遵循 Ben Klemer 的“更好的可变参数”内容 的方法。我不会使用他的所有方法,但我会尝试生成一个结构,该结构将函数的 arg 签名表示为结构。然后我可以尝试获取结构元素的大小(甚至结构本身,如果我关心的是对空间的保守估计)。这有几个问题。首先,它可能只适用于 C99 兼容的东西(仍在检查)。其次,它导致为每个实现的功能创建一个额外的结构。这不完全是一个问题,但仍然存在一个问题,即他创建结构体的方法最终与函数具有相同的名称(因此您仍然需要引用名称来使用它们)。不过我也许可以解决这个问题。

第三种可能的方法是递归宏,尽管我不确定这会让编译器有多高兴。理论上可以通过调用 POPPER(arg, ...) POPPER(VA_ARGS) + sizeof(arg 形式的宏,以递归方式从 VA_ARGS 中弹出元素)。显然,当 VA_ARG 为空时,它需要一个停止规则(以及确保您不会被浮动 + 符号捕获的东西),但您明白了。

其中任何一件事都可以让我做到这一点:

  1. 从可变参数宏中解压 VA_ARGS 的良好、灵活的方法。如果有任何方法可以对其进行索引,
  2. 这是一个可以依靠的递归宏的好例子(以及它在最大参数数、编译器兼容性、标准合规性等方面的限制)。
  3. 一种通过不同类型的函数检查直接获取所有参数总大小的方法。 GCC 似乎有一些用于构造函数的 疯狂 函数对可能适用的呼叫转发的调用,但这些是特定于编译器的,几乎没有记录,并且似乎没有报告它们分配的内存块的大小。他们还报告大量不相关的信息。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无人接听 2024-11-18 23:03:38

所以这就是我最终想到的,它最多可以运行 64 个参数(或者实际上是您愿意定义的 FOR 和 COUNT_ARGS 函数的数量)。所以,感谢您的帮助。希望这些花絮对其他人有帮助——它们代表了我对网络上传播的一些伟大想法的整理。

我使 FOR_EACH 结构更加通用,这样我就可以将它用于其他东西(例如乘法等,只需更改前缀和后缀即可)。


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

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__)
雨落□心尘 2024-11-18 23:03:37

您需要一个 FOREACH 宏,它允许在可变参数列表的每个元素上扩展另一个宏。这是通过为每个感兴趣的列表长度定义变体来实现的:

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

一旦有了它,您就可以编写一个宏来获取其参数的大小并将它们链接在一起以添加它们:

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

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));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文