Is there a way to reject pointer comparison in C ++?

I have a (working) codebase where I want to add something like a member is_equivalent

to the class hierarchy. Scattered throughout the codebase there are comparisons like

if (foo == bar) ...

      

where foo

and bar

are normal pointers to objects in the class hierarchy. I would like to introduce the following: (as a virtual function in the base class):

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

      

so the concept of "equality" is weakened. A concrete geometric example would be a hierarchy of figures, where a Circle

should be considered equivalent Ellipse

with the same major and minor axes (not a perfect analogy).

What I would like to do is get the compiler to help me find all the instances where I did a direct pointer comparison. I thought I should have provided something like operator==(const Shape *, const Shape *)

, but that isn't even allowed by C ++.

Some pointer comparisons may require pointer comparisons, but some of them will need to be replaced with a virtual method call. I need to look at everyone. What approaches are there for identifying all such comparisons? Temporary violation by string or execution ok. There is pretty good coverage.

I read the C ++ Trick question to avoid comparing pointers , which is similar but more limited because the accepted answer assumes the existence of a factory class.

+3


source to share


1 answer


You can write your own code analysis tool. Here's a minimal (and rather trivial) example I built with libclang

. This filters out every binary operator in the source. With this, you can collect all pointer equality comparisons with 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;
}

      

The example file I used was this imaginary C ++ source file (named 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;
}

      

For what my tool printed this:

Comparison: f == f
Comparison: b == b
Comparison: f == b

      

+5


source







All Articles