【问题标题】:Use C/C++ Macros to Generate Function Signature使用 C/C++ 宏生成函数签名
【发布时间】:2020-07-14 20:37:25
【问题描述】:

我正在尝试使用 C/C++ 中的宏来生成一些样板函数声明和定义。

我想要一个类似的宏:

DECLARE_FUNCTION(myFunction, int, A, int, B, char, C)

生成以下代码(请忽略此代码似乎毫无意义的事实,这只是一个简化示例)

void myFunction(int A, int B, char C) {
    myFunction_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()});
}
void myFunction_PROXY(const Variant (&args)[4]) {
    myFunction_HANDLER(args[0], args[1], args[2]);
}
void myFunction_HANDLER(int A, int B, char C) {

}

请注意我需要函数签名(带有类型和名称)以及要在 Variant() 构造函数中使用的名称。因此,我假设我需要有选择地“循环”通过宏的 VA_ARGS 值来获得不同的参数组合并以不同的方式应用它们。

我已经定义了一个名为 Variant 的类。

根据我的研究,我相信这涉及“递归宏”的某种组合,但是,我似乎无法理解如何获得我想要的输出。

谁能帮助我,或者至少给我一个很好的解释,说明 C 中的递归宏是如何工作的?

谢谢

【问题讨论】:

  • C 中的 class 叫什么?
  • 我不确定为什么在这种情况下需要递归宏?
  • 我认为您的意思是 __VA_ARGS__ 宏,例如something like this.
  • 是否有机会只使用 Python/Perl/C# 的 C 文件并让它在需要时处理代码生成?这是一个外部步骤,但可以在未来更好地保持输出。

标签: c++ macros


【解决方案1】:

使用预处理器遍历逗号分隔的列表需要编写样板宏。

通常您必须编写或生成至少 O(n) 宏来处理最长为 n 元素的列表。 @Human-Compiler 的答案是 O(n<sup>2</sup>)

您可以从 Boost.Preprocessor 获得类似的宏,或将其用作灵感。

或者您可以为列表使用不同的语法:

DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C))

然后您可以处理任何大小的列表,并使用固定数量的宏:

#define DECLARE_FUNCTION(func_, seq_) \
    void myFunction(END(PARAMS_LOOP_0 seq_)) { \
        myFunction_PROXY(
            (Variant[1 END(COUNT_LOOP_A seq_)]){END(VAR_LOOP_A seq_) Variant()}); \
    } \
    void myFunction_PROXY(const Variant (&args)[1 END(COUNT_LOOP_A seq_)]) { \
        const int x = __COUNTER__+1; \
        myFunction_HANDLER(END(ARR_LOOP_0 seq_)); \
    } \
    void myFunction_HANDLER(END(PARAMS_LOOP_0 seq_)) {}
    
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
    
#define PARAMS_LOOP_0(type_, name_)   PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
#define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
#define PARAMS_LOOP_0_END
#define PARAMS_LOOP_A_END
#define PARAMS_LOOP_B_END
#define PARAMS_LOOP_BODY(type_, name_) type_ name_

#define COUNT_LOOP_A(...) COUNT_LOOP_BODY COUNT_LOOP_B
#define COUNT_LOOP_B(...) COUNT_LOOP_BODY COUNT_LOOP_A
#define COUNT_LOOP_A_END
#define COUNT_LOOP_B_END
#define COUNT_LOOP_BODY +1

#define VAR_LOOP_A(type_, name_) VAR_LOOP_BODY(name_) VAR_LOOP_B
#define VAR_LOOP_B(type_, name_) VAR_LOOP_BODY(name_) VAR_LOOP_A
#define VAR_LOOP_A_END
#define VAR_LOOP_B_END
#define VAR_LOOP_BODY(name_) Variant(name_), 

#define ARR_LOOP_0(...)   ARR_LOOP_BODY ARR_LOOP_A
#define ARR_LOOP_A(...) , ARR_LOOP_BODY ARR_LOOP_B
#define ARR_LOOP_B(...) , ARR_LOOP_BODY ARR_LOOP_A
#define ARR_LOOP_A_END
#define ARR_LOOP_B_END
#define ARR_LOOP_BODY args[__COUNTER__-x]

使用这些宏,DECLARE_FUNCTION(myFunction, (int,A)(int,B)(char,C)) 扩展为:

void myFunction(int A, int B, char C)
{
    myFunction_PROXY((Variant[1+1+1+1]){Variant(A), Variant(B), Variant(C), Variant()});
}

void myFunction_PROXY(const Variant (&args)[1+1+1+1])
{
    const int x = 0+1;
    myFunction_HANDLER(args[1-x], args[2-x], args[3-x]);
}

void myFunction_HANDLER(int A, int B, char C) {}

注意使用__COUNTER__。它不是标准 C++ 的一部分,但主要编译器支持它作为扩展。除了编写样板宏之外,您没有任何其他选择来获取连续数组索引。

【讨论】:

    【解决方案2】:

    免责声明 1:

    老实说,我不建议你在宏后面做这样的机器。它们将成为巨大的维护负担,回报微乎其微。如果您可以使用其他更易于维护的抽象来做到这一点,那么从长远来看对您会更好。

    免责声明 2:

    我现在正在写这个没有编译器。我可能有语法错误,但这些是构建此解决方案的通用构建块。


    免责声明说,这是可以做到的——但不是很好。

    这里有几个问题需要用大量的宏技巧来解决:

    • 您希望扩展包含__VA_ARGS__ / 2 的大小(在扩展时)
    • 您希望__VA_ARGS__ 的扩展产生Variant(&lt;arg 1&gt;), Variant(&lt;arg 3&gt;), Variant(&lt;arg 5&gt;), Variant()
    • 您希望__VA_ARGS__ 的扩展产生Variant[size]{args[0], args[1], args[2], ...}

    首先,我将创建一个名为 JOIN 的助手:

    #define JOIN(a, b) JOIN_H(a, b)
    #define JOIN_H(a, b) a ## b
    

    这可能看起来很傻,但实际上它的作用是确保被连接在一起的宏在被连接之前会被求值 -- 这样被调用的宏函数就会正确地用它们的 实例化一个连接。 em>result 而不是全名。

    获取__VA_ARGS__ / 2的大小

    获取__VA_ARGS__ 的大小通常需要两个宏:

    • __VA_ARGS__, N, N-1, N-2, ... 传递到辅助宏中,并且
    • 另一个在末尾提取 N

    类似:

    #define COUNT_VA_ARGS(...) \
      COUNT_VA_ARGS_H(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
    #define COUNT_VA_ARGS_H(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N,...) N
    

    这是有效的,因为第一个传递了__VA_ARGS__ 的所有参数并从第 N 个数字倒数,然后我们提取N

    在您的情况下,您需要__VA_ARGS__ / 2,因此您需要将这些参数加倍

    #define COUNT_VA_ARGS(...) \
      COUNT_VA_ARGS_H(__VA_ARGS__, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0)
    #define COUNT_VA_ARGS_H(_1, _1, _2, _2, _3, _3, _4, _4, _5, _5, _6, _6, _7, _7, _8, _8, _9, _9, _10, _10, N,...) N
    

    使__VA_ARGS__ 产生Wrap(&lt;arg 1&gt;), Wrap(&lt;arg 3&gt;), ...

    与 C++ 可变参数模板不同,宏不能具有可以包装每个参数的扩展表达式。要在宏中模拟这一点,您几乎必须明确定义 N 个扩展,然后调用它,您需要组合一个宏的结果来调用它。

    #define WRAP_VA_ARGS_0(wrap)
    #define WRAP_VA_ARGS_1(wrap,x0) wrap(x0)
    ...
    #define WRAP_VA_ARGS_10(wrap,x0,x1, ..., x10) wrap(x0), wrap(x1), ..., wrap(x10)
    
    // Call into one of the concrete ones above
    #define WRAP_VA_ARGS(wrap, __VA_ARGS__) JOIN(WRAP_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS__))(__VA_ARGS__)
    

    由于表达式实际上需要其中的每个 other 参数,因此您将再次需要将参数加倍:

    #define WRAP_VA_ARGS_0(wrap)
    #define WRAP_VA_ARGS_1(wrap,x0type,x0) wrap(x0)
    #define WRAP_VA_ARGS_2(wrap,x0type,x0,x1type,x1) wrap(x0), wrap(x1)
    ...
    

    现在调用 WRAP_VA_ARGS(Variant, int, A, float, B) 将创建 Variant(A), Variant(B)

    创建索引值列表

    与上面的包装类似,您需要找到一种方法来生成数字列表并将其包装。同样,这必须委托给计数包装器

    #define WRAP_COUNT_VA_ARGS_0(wrap)
    #define WRAP_COUNT_VA_ARGS_1(wrap) wrap[0]
    #define WRAP_COUNT_VA_ARGS_2(wrap) wrap[0], wrap[1]
    ...
    
    #define WRAP_COUNT_VA_COUNT_ARGS(wrap, ...) JOIN(WRAP_COUNT_VA_ARGS_, COUNT_VA_ARGS(__VA_ARGS))(wrap)
    

    调用WRAP_COUNT_VA_COUNT_ARGS(args, int, A, float, B) 应该生成args[0], args[1]

    把它们放在一起

    触发警告:这会很糟糕

    #define DECLARE_FUNCTION(name, ...) \  
    void name(__VA_ARGS__) {            \
        JOIN(name, _PROXY)((Variant[COUNT_VA_ARGS(__VA_ARGS__)+1]) {WRAP_VA_ARGS(Variant,__VA_ARGS__), Variant()}); \
    } \
    void JOIN(name, _PROXY)(const Variant (&args)[COUNT_VA_ARGS(__VA_ARGS__) + 1]) { \
        JOIN(name, _HANDLER)(WRAP_COUNT_VA_COUNT_ARGS(args, __VA_ARGS__)); \
    } \
    void JOIN(name, _HANDLER)(__VA_ARGS__) { \
     \
    } 
    

    如果运气好的话,DECLARE_FUNCTION(myFunction, int, A, int, B, char, C) 的示例应该会产生:

    void myFunction(int A, int B, char C) {
        myFunction_PROXY((Variant[3+1]{Variant(A), Variant(B), Variant(C), Variant()});
    }
    void myFunction_PROXY(const Variant (&args)[3+1]) { 
        myFunction_HANDLER(args[0], args[1], args[2]);
    }
    void myFunction_HANDLER(int A, int B, char C) {
        
    }
    

    注意:数组是由常量表达式3 + 1创建的,因为我们需要做这个算术来解释myFunction_PROXY调用结束时的Variant()


    不要做宏。宏很糟糕,嗯嗯?

    【讨论】:

      【解决方案3】:

      我发现您的所有回复都非常有用;就我而言,我需要保护一组作为旧应用程序的一部分实现的例程。

      约束:互斥,尽量减少代码的变化。

      “MTX_DB_PROTECTED_FUNCTION”宏效果很好。

      #define END(...) END_(__VA_ARGS__)
      #define END_(...) __VA_ARGS__##_END
      
      #define PARAMS_LOOP_0(type_, name_)   PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
      #define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
      #define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
      #define PARAMS_LOOP_0_END
      #define PARAMS_LOOP_A_END
      #define PARAMS_LOOP_B_END
      #define PARAMS_LOOP_BODY(type_, name_) type_ name_
      
      #define VAR_LOOP_0(type_, name_)   VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
      #define VAR_LOOP_A(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_B
      #define VAR_LOOP_B(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
      #define VAR_LOOP_0_END
      #define VAR_LOOP_A_END
      #define VAR_LOOP_B_END
      #define VAR_LOOP_BODY(type_, name_) name_
      
      //https://stackoverflow.com/questions/62903631/use-c-c-macros-to-generate-function-signature
      #define MTX_DB_PROTECTED_FUNCTION(type_, func_, seq_) \
      \
      static type_ _s_mtx_##func_##_protected(END(PARAMS_LOOP_0 seq_));\
      \
      type_ func_(END(PARAMS_LOOP_0 seq_))\
      {\
          UTL_AcquireMutex(__FUNCTION__, &g_h_dataFileMutex, OSL_TIMEOUT_INFINITE);\
          type_ ret = _s_mtx_##func_##_protected(END(VAR_LOOP_0 seq_));\
          UTL_ReleaseMutex(__FUNCTION__, &g_h_dataFileMutex);\
          return ret;\
      }\
      \
      \
      static type_ _s_mtx_##func_##_protected(END(PARAMS_LOOP_0 seq_))
      

      示例

      原函数

      int dummyfunc(char TabId, char checksum)
      {
          return 0;
      }
      

      宏插入

      MTX_DB_PROTECTED_FUNCTION(int, dummyfunc, (char,TabId)(char,checksum))
      {
          return 0;
      }
      

      宏展开

      static int  _s_mtx_dummyfunc_protected(char  TabId , char  checksum );        
                                                                                    
      int  dummyfunc(char  TabId , char  checksum )                                 
      {                                                                             
          UTL_AcquireMutex(__FUNCTION__, &g_h_dataFileMutex, (unsigned long)(-1));  
          int ret = _s_mtx_dummyfunc_protected(TabId , checksum );                  
          UTL_ReleaseMutex(__FUNCTION__, &g_h_dataFileMutex);                       
          return ret;                                                               
      }                                                                             
                                                                                    
      static int  _s_mtx_dummyfunc_protected(char  TabId , char  checksum )
      {
          return 0;
      }
      

      对于无参数函数

      MTX_DB_PROTECTED_FUNCTION(int, dummyWoParams,(,))
      {
      

      【讨论】:

        【解决方案4】:

        基于许多其他答案和 cmets,这是我想出的解决我的特定问题的方法:

        #define END(...) END_(__VA_ARGS__)
        #define END_(...) __VA_ARGS__##_END
        
        #define PARAMS_LOOP_0(type_, name_)   PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
        #define PARAMS_LOOP_A(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_B
        #define PARAMS_LOOP_B(type_, name_) , PARAMS_LOOP_BODY(type_, name_) PARAMS_LOOP_A
        #define PARAMS_LOOP_0_END
        #define PARAMS_LOOP_A_END
        #define PARAMS_LOOP_B_END
        #define PARAMS_LOOP_BODY(type_, name_) type_ name_
        
        #define VAR_LOOP_0(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
        #define VAR_LOOP_A(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_B
        #define VAR_LOOP_B(type_, name_) , VAR_LOOP_BODY(type_, name_) VAR_LOOP_A
        #define VAR_LOOP_0_END
        #define VAR_LOOP_A_END
        #define VAR_LOOP_B_END
        #define VAR_LOOP_BODY(type_, name_) name_
        
        //------------------------------------------------------------------------------
        
        #define DEFINE_SLOT(func_, ...) \
            void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) { invoke(this, &func_##_SLOT END(VAR_LOOP_0 __VA_ARGS__)); }\
            void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
        
        #define DECLARE_SLOT(func_, ...) \
            void func_(END(PARAMS_LOOP_0 __VA_ARGS__));\
            void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
        
        #define DECLARE_VIRTUAL_SLOT(func_, ...) \
            virtual void func_(END(PARAMS_LOOP_0 __VA_ARGS__));\
            virtual void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__))
        
        #define DECLARE_SLOT_INTERFACE(func_, ...) \
            virtual void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) = 0;\
            virtual void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__)) = 0
        
        #define DECLARE_SLOT_OVERRIDE(func_, ...) \
            void func_(END(PARAMS_LOOP_0 __VA_ARGS__)) override;\
            void func_##_SLOT(END(PARAMS_LOOP_0 __VA_ARGS__)) override
        

        我通过调用这些“插槽”来采用 Qt 的一些命名约定。

        这些宏将按如下方式使用:

        //In the .h file
        class MyBase {
        public:
          DECLARE_SLOT_INTERFACE(foo, (int, a) (int, b));
        };
        
        class MyClass : public MyBase {
        public:
          DECLARE_SLOT_OVERRIDE(foo, (int, a) (int, b));
          DECLARE_SLOT(bar, (bool, a) (const MyClass& obj));
        };
        
        //In the .cpp file
        DEFINE_SLOT(MyClass::foo, (int, a) (int, b)) {
        
        }
        
        DEFINE_SLOT(MyClass::bar, (bool, a) (const MyClass& obj)) {
        
        }
        

        【讨论】:

          【解决方案5】:

          不是很清楚它会影响你的情况,你可以用__VA_ARGS__做你想做的事

          #define DECLARE_FUNCTION(myFunction, ...)                                     \
          static void myFunction##_HANDLER(__VA_ARGS__) {                               \
                                                                                        \
          }                                                                             \
          \
          static void myFunction##_PROXY(const Variant (&args)[4]) {                    \
              myFunction##_HANDLER(args[0], args[1], args[2]);                          \
          }                                                                             \
          \
          static void  myFunction(__VA_ARGS__) {                                        \
              myFunction##_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()}); \
          }
          
          DECLARE_FUNCTION(myFunction, int A, int B, char C)
          

          会生成

          
          static void myFunction_HANDLER(int A, int B, char C) { } 
          static void myFunction_PROXY(const Variant (&args)[4]) { 
              myFunction_HANDLER(args[0], args[1], args[2]);
          }
          static void myFunction(int A, int B, char C) {
           myFunction_PROXY((Variant[4]){Variant(A), Variant(B), Variant(C), Variant()});
          }
          

          【讨论】:

          • 这里的问题是 myFunction 的宏代码有 Variant(A), Variant(B), ... 硬编码到 PROXY 函数的调用中。 A, B, ... 是用户传递给宏的变量的名称,因此也必须生成这些变量。
          • 参数数量和类型可以不同?
          • 我看到您将其从 C 更改为 C++,对我来说,在 C++ 中有这种需要通过 MACRO 完成是很奇怪的。你可以使用 FOR_EACH 就像在这里做的那样:stackoverflow.com/a/11994395
          猜你喜欢
          • 2012-07-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-26
          • 2023-03-30
          • 2020-11-22
          相关资源
          最近更新 更多