C++中有没有禁止指针比较的方法?

3

我有一个(可运行的)代码库,我想在类层次结构中添加类似于is_equivalent的成员。在代码库中散布着像这样的比较:

if (foo == bar) ...

foobar是类层次结构中普通对象的指针。我想介绍以下用法(作为基类中的虚函数):

if (foo->is_equivalent(bar)) ...

为了放宽“相等”的概念,一个具体的几何例子可能是形状层次结构,其中应将“圆”视为与具有相等长轴和短轴的“椭圆”等效(这不是完美的类比)。

我想做的是让编译器帮助我找到所有直接指针比较的实例。我想过提供像operator==(const Shape *, const Shape *)这样的东西,但C++甚至不允许这样做。

一些指针比较可能需要保持指针比较,但有些指针比较需要改成虚方法调用。我需要仔细查看每一个。有哪些方法可以识别所有这些类型的比较?暂时中断构建或执行都可以。测试覆盖率非常好。

我已经阅读了类似的问题C++ Trick to avoid pointer comparison,但更加有限,因为被接受的答案假定存在一个工厂类。


2
你是否认为编写一个定制的静态分析工具并在代码库上运行(可能是定期地)是可行的选择?libclang有一个相当完整的AST API,我认为你可以使用它来过滤所有指针比较。 - The Paramagnetic Croissant
@TheParamagneticCroissant:这是一个可行的想法,了解libclang本身也可能很有趣。 - Greg Hewgill
1
你的问题示例并没有太多意义。if (foo == bar) 是在比较对象 foo 是否为 bar 相同的对象,这与 char *foo = "abc"; char bar[] = "abc"; if (foo == bar) 相同 - 这是不正确的,因为 foobar 指向内存中的不同位。如果你想要比较 *foo == *bar,那么你可以通过实现 operator== 在C ++中完成你希望做的事情。 - Mats Petersson
@MatsPetersson:仅仅更改单例对象的名称并不能帮助识别某个指针“foo”与另一个指针“bar”进行比较的位置。 - Greg Hewgill
1
你知道,这段代码本可以用很多不同的方式编写。我并不是在寻求能够避免当前问题的额外策略,而是在寻求帮助我走出当前困境的策略。 - Greg Hewgill
显示剩余8条评论
1个回答

5
您可以编写一个定制的代码分析工具。以下是我使用 libclang 构建的最小(而且相当琐碎)示例。它会过滤源代码中的每个二元运算符。通过不断完善,您可以从 AST 中收集所有指针相等比较操作。
#include <clang-c/Index.h>
#include <stdio.h>

static void printBinOp(CXCursor cursor)
{
    CXSourceRange range = clang_getCursorExtent(cursor);
    CXSourceLocation begin = clang_getRangeStart(range);
    CXSourceLocation end = clang_getRangeEnd(range);
    CXFile file;
    unsigned begin_offset, end_offset, length;

    // retrieve physical location of AST node
    clang_getSpellingLocation(begin, &file, NULL, NULL, &begin_offset);
    clang_getSpellingLocation(end, NULL, NULL, NULL, &end_offset);
    length = end_offset - begin_offset;

    // Open the file, error checking omitted for clarity
    CXString xfname = clang_getFileName(file);
    const char *fname = clang_getCString(xfname);
    FILE *fhndl = fopen(fname, "r");
    clang_disposeString(xfname);

    // Read the source
    char buf[length + 1];
    fseek(fhndl, begin_offset, SEEK_SET);
    fread(buf, length, 1, fhndl);
    buf[length] = 0;
    fclose(fhndl);

    // and print it
    printf("Comparison: %s\n", buf);
}

static enum CXChildVisitResult ptrCompVisitor(CXCursor cursor, CXCursor parent, CXClientData client_data)
{
    if (clang_getCursorKind(cursor) == CXCursor_BinaryOperator) {
        printBinOp(cursor);
    }

    return CXChildVisit_Recurse;
}

int main()
{
    CXIndex index = clang_createIndex(0, 0);
    CXTranslationUnit tu = clang_parseTranslationUnit(index, "foo.cpp", NULL, 0, NULL, 0, CXTranslationUnit_None);

    clang_visitChildren(clang_getTranslationUnitCursor(tu), ptrCompVisitor, NULL);

    clang_disposeTranslationUnit(tu);
    clang_disposeIndex(index);
    return 0;
}

我使用的示例文件是这个想象中的C++源代码文件(命名为foo.cpp):
class Foo {
    int foo;
};

class Bar {
    int bar;
}

int main()
{
    void *f = new Foo();
    void *b = new Bar();

    bool alwaystrue_1 = f == f;
    bool alwaystrue_2 = b == b;

    return f == b;
}

我的工具打印出了这个:
Comparison: f == f
Comparison: b == b
Comparison: f == b

1
谢谢,看起来这是一个很好的开始! - Greg Hewgill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接