【问题标题】:Determine if same pointers are passed to a macro确定是否将相同的指针传递给宏
【发布时间】:2013-02-28 07:52:00
【问题描述】:

有一组宏,用于调试、日志记录、堆栈跟踪显示等。其中一个是:

#define ASSERT_IF_NULL_2(_ptr1, _ptr2) \ 
    ASSERT(_ptr1);  \
    ASSERT(_ptr2);

这是我编写的宏的简化版本。如果断言(运行时)失败,我有自定义断言对话框,将此类空检查失败记录到日志文件中。还编写了宏,以便在编译时检查非指针(静态断言)。

现在,我正在寻找一些静态断言来检查两个指针​​是否实际上相同。示例:

int* ptr;
ASSERT_IF_NULL_2(ptr, ptr);

应该引发编译器错误,因为宏的两个参数相同。我不在乎指针是否指向相同的内存(因为那是运行时)。

我试过这样的表达方式:

int xx;
xx = 1 / (ptr-ptr);
xx = 1 / (&ptr - &ptr);

它们都没有给出被零除的编译器错误。 另外,我尝试过使用带有void* 的模板是模板参数:

template<void* T>
class Sample{};

但它不允许将局部变量指针传递给模板非类型参数。

我使用的是VC9,它不支持constexpr关键字(甚至VS2012也不支持)。我尝试使用 'const' 代替,这不会引发错误。我还将表达式用作数组大小,这总是会导致错误。

int array[(&ptr - &ptr)]; // Even with ptrdiff_t

【问题讨论】:

  • 当你说“两个指针实际上是相同的”时,你到底是什么意思? _ptr1_ptr2 包含相同的标记序列?
  • 这意味着如果它们实际上是相同的。 foo(a,a) 表示这些参数相同。
  • int array[(&amp;ptr - &amp;ptr)]; 是一个很好的尝试,但如果 size-expression 为负数怎么办?这意味着,指针不同,因此有效,但数组声明会导致错误。
  • (bool)(&amp;ptr-&amp;ptr) 将前来救援。都可以搞定!
  • @Ajay:是的。我只是指出来。我知道如果表达式是恒定的(不幸的是不是),我们可以让它以多种不同的方式工作。

标签: c++ templates visual-c++ compiler-errors static-assert


【解决方案1】:

好的,解决方案很简单。使用static_assertStringizing Operator (#)

#define ASSERT_TWO(a,b) static_assert((void*)#a != (void*) #b, "'" #a  "' and '" #b "' are same variables passed to ASSERT_TWO.") 

演示:

int *global;    

int main()
{
    int *local;

    ASSERT_TWO(global, local);  //should pass
    ASSERT_TWO(local, global);  //should pass

    ASSERT_TWO(global, global); //should fail
    ASSERT_TWO(local, local);   //should fail

    (void)local; //suppress unsused warning!
}

这会导致非常有用错误消息:

main.cpp:在函数'int main()'中:
main.cpp:18:5:错误:静态断言失败:“全局”和“全局”是传递给 ASSERT_TWO 的相同变量。
main.cpp:19:5: 错误:静态断言失败:“local”和“local”是传递给 ASSERT_TWO 的相同变量。

Online demo.

希望对您有所帮助。

我记得这对您没有帮助,因为您使用的是 VC9,但我保留此答案,因为它可能对其他人有所帮助。到目前为止,您可以使用其他生成重新声明错误的解决方案,与static_assert 消息相比,该错误消息的帮助较小。

【讨论】:

  • main.cpp:15:5: error: redeclaration of 'constexpr const int x' 怎么样,更不用说如果我将它切换到应该可以工作的 ASSERT_TWO(local, global) 会出现更多错误。
  • 这是一个固定版本:#define ASSERT_TWO(a, b) {int x = 1 / (reinterpret_cast&lt;std::ptrdiff_t&gt;(a) - reinterpret_cast&lt;std::ptrdiff_t&gt;(b));}。 => 你不需要将指针定义为 const expr,重新定义的错误就消失了
  • @ted: int x 不会强制编译器评估 RHS 上的表达式,即使它可以。
  • 您在结束 } 之前缺少一个分号。好的,我明白你为什么要使用constexpr 但是this 呢:全局的值不能在constexpr 中使用
  • 对不起。修改后的解决方案也不起作用。我不明白为什么在(void*) 之后使用#pp 不同?
【解决方案2】:

所以您想检查是否将相同的名称传递给两个参数,对吗?那么这个简单的技巧就奏效了

#define ASSERT_IF_NULL_2(_ptr1, _ptr2)\
{\
    int _check_##_ptr1;\
    int _check_##_ptr2;\
}\
ASSERT(_ptr1);\
ASSERT(_ptr2);

int main ()
{
    int* ptr1;
    int* ptr2;

    ASSERT_IF_NULL_2 (ptr1, ptr1); // error: redeclaration of `int _check_ptr1'
    ASSERT_IF_NULL_2 (ptr1, ptr2); // OK
}

编辑(由 OP,Ajay):

在 Visual C++ 编译器上,我们可以使用 MS 特定关键字__if_exists 通过static-assert 给出错误:

 #define ASSERT_IF_NULL_2(_ptr1, _ptr2)\
    {\
        int _check_##_ptr1;\
        __if_exists(_check_##_ptr2) 
             STATIC_ASSERT(false, "Same pointer passed twice")
    }\

由于 VS2010 之前的编译器不存在 static_assert 关键字,因此可以使用自定义编写的 STATIC_ASSERT。

【讨论】:

  • 嗯,这是一个很好的解决方案,对我有用。我正在更新您的帖子本身,它将使用静态断言(仅适用于 VC 编译器)。
【解决方案3】:

您是在检查指针是否指向相同的内存,或者它们是否指向相同的值?

第一种情况

xx = 1 / (ptr-ptr);

应该可以。

第二种情况

xx = 1 / (*ptr-*ptr);

应该可以。

【讨论】:

  • 你没看懂问题!
【解决方案4】:

不妨试试其中一种:

ASSERT_IF_NULL(ptr1 == ptr1);
ASSERT(ptr1 != ptr2)

正如 Nawaz 所指出的,你想要一个编译错误,所以试试这个:

1/(ptr1 != ptr2);
1/static_cast<int>(ptr1 != ptr2);
static_assert(ptr1 != ptr2, "Pointers are different")       //if you dont use c++0x look here:http://stackoverflow.com/a/1664651/258418

【讨论】:

  • 它不会工作。而且您的 static_assert 关键字用法是错误的 - 它需要两个参数。
  • @Ajay:你看到我的尝试了吗(在我删除它之前)?它部分解决了。我在做这个工作。如果我成功解决它,我会再次发布。
  • 我试过了,并尝试添加评论:我使用的是 VC9,它没有 constexpr。查看修改后的帖子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 2021-03-10
  • 1970-01-01
  • 2015-05-09
  • 1970-01-01
  • 1970-01-01
  • 2018-08-27
相关资源
最近更新 更多