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.
source to share
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
source to share