【问题标题】:Possible to define a function-like macro with a variable body?可以用变量体定义类似函数的宏吗?
【发布时间】:2011-05-01 12:31:53
【问题描述】:

我一直在查看用于定义宏的 GCC docs,看起来我想要的东西是不可能的,但我想如果是的话,这里有人会知道。

我要做的是定义这个宏:

synchronized(x) {
  do_thing();
}

扩展为:

{
    pthread_mutex_lock(&x);
    do_thing();
    pthread_mutex_unlock(&x);
}

在 C++ 中,我可以创建一个 SynchronizedBlock 对象,该对象在其构造函数中获取锁并在析构函数中解锁,但我不知道如何在 C 中执行此操作。

我意识到我可以使用 synchronized(x, &myfunction); 形式的函数指针,但我的目标是让一些 C 代码看起来尽可能像 Java。是的,我知道这是邪恶的。

【问题讨论】:

  • 在尝试使一种语言看起来像另一种语言之前,您真的应该三思而后行。看起来像您所要求的语法的宏对于将其读取为 C 的人来说会非常混乱。
  • R.这是正确的,这可能会使事情变得难以阅读,因为不容易看出这是一个宏。我认为最好的做法是遵守宏应该是大写的约定,并且名称应该是真正的信息。我会选择MUTUALY_EXCLUDE 之类的东西。
  • ^And yes, I know this is evil.

标签: c macros mutex


【解决方案1】:

编辑:更改为 nategoose 的版本

#define synchronized(lock) \
for (pthread_mutex_t * i_#lock = &lock; i_#lock; \
     i_#lock = NULL, pthread_mutex_unlock(i_#lock)) \
    for (pthread_mutex_lock(i_#lock); i_#lock; i_#lock = NULL)

你可以这样使用它:

synchronized(x) {
    do_thing(x);
}

甚至不用大括号

synchronized(x)
    do_thing();

【讨论】:

  • 这既令人惊奇又令人恐惧。我从来没想过。
  • 这应该解决这两个问题:#define synchronized(lock) \ for (pthread_mutex_t * i_ = &lock; i_; i_ = NULL) \ ` for (pthread_mutex_lock(i_); i_;` \ ` pthread_mutex_unlock(i_), i_ = NULL)`
  • 或者,使用单个 for() 并检查是否已获得锁:#define synchronized(MUTEX) \ for(pthread_mutex_t *synchronized_mutex_ = &MUTEX; \ synchronized_mutex_ && !pthread_mutex_lock(synchronized_mutex_); \ pthread_mutex_unlock(synchronized_mutex_), synchronized_mutex_ = 0)
  • 另外,这有一些关于流量控制的陷阱。 breakcontinuegotoreturn 可能与程序员预期的不同:前两个将仅终止宏内部的 for,而不是封闭结构。后面两个会跳过pthread_mutex_unlock,所以锁不会被释放。
  • 这个版本不会让你嵌套同步块。为了解决这个问题,我将i_ 更改为i_##lock(因此synchronized(some_lock) 扩展为for(pthread_mutex_t *i_some_lock = &some_lock; ...。作为奖励,synchronized(some_lock) synchronized(some_lock) {} 将导致编译器错误。
【解决方案2】:

这是一个开始,但您可能需要对其进行调整:

#define synchronized(lock, func, args...) do { \
    pthread_mutex_lock(&(lock)); \
    func(##args); \
    pthread_mutex_unlock(&(lock)); \
} while (0)

像这样使用(不幸的是,不是你想要的类似 Java 的语法):

synchronized(x, do_thing, arg1, arg2);

【讨论】:

  • 看起来do ... while(0) 不是必需的,{ .. } 单独工作(至少与 gcc 一起使用)。这仍然需要像synchronized(x, function) 这样的块而不是synchronized(x){ /* function */ }
【解决方案3】:

这是我想出的最好的:

#define synchronized(x, things) \
      do { \
           pthread_mutex_t * _lp = &(x); \
           pthread_mutex_lock(_lp);      \
           (things);                     \
           pthread_mutex_unlock(_lp);    \
      } while (0)

...

        synchronized(x,(
                          printf("hey buddy\n"),
                          a += b,
                          printf("bye buddy\n")
                        ));

请注意,您必须使用很少使用的逗号运算符,并且对哪些代码可以存在于(不太类似于 java)同步代码列表中存在限制。

【讨论】:

    【解决方案4】:

    非常有趣的问题!

    我查看了其他答案并喜欢使用for 的答案。如果可以的话,我有一个改进! GCC 4.3 引入了 COUNTER 宏,我们可以使用它来生成唯一的变量名。

    #define CONCAT(X, Y) X##__##Y
    #define CONCATWRAP(X, Y) CONCAT(X, Y)
    #define UNIQUE_COUNTER(prefix) CONCATWRAP(prefix, __COUNTER__)
    
    #define DO_MUTEX(m, counter) char counter; \
    for (counter = 1, lock(m); counter == 1; --counter, unlock(m))
    
    #define mutex(m) DO_MUTEX(m, UNIQUE_COUNTER(m))
    

    使用这些宏,这段代码...

    mutex(my_mutex) {
        foo();
    }
    

    ...将扩展为...

    char my_mutex__0;
    for (my_mutex__0 = 1, lock(my_mutex); my_mutex__0 == 1; --my_mutex__0, unlock(m)) {
        foo();
    }
    

    my_mutex__n 从 0 开始,每次使用时都会生成一个新名称!您可以使用相同的技术来创建类似于监视器的代码体,并为互斥体使用唯一但未知的名称。

    【讨论】:

    • 我喜欢这种方法,但如果我传递一个结构成员,它就不起作用。 synchronized(object->lock) { /*stuff*/ } 扩展为 char object->lock__0; /*etc*/,这会导致问题,因为 lock__0 不是结构成员。
    • 啊,真的 =( 好吧,无论如何,上面修改后的答案看起来更好,并且不需要计数器。干杯!
    • 只命名变量_lock_ ## __COUNTER__ 似乎有效(并解决了其他实现中的问题)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-14
    • 2015-10-29
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多