Reputation: 993451
I have a (working) code base where I want to add something like an is_equivalent
member to a class hierarchy. Scattered throughout the code base there are comparisons like
if (foo == bar) ...
where foo
and bar
are ordinary pointers to objects in the class hierarchy. I would like to introduce usage like the following (as a virtual function in the base class):
if (foo->is_equivalent(bar)) ...
so that the notion of "equality" is relaxed. A concrete geometric example might be a shape hierarchy, where a Circle
should be considered equivalent to an Ellipse
with equal major and minor axes (not a perfect analogy).
What I would like to do is have the compiler help me find all the instances where I have done direct pointer comparison. One thought I had was to provide something like an operator==(const Shape *, const Shape *)
but that isn't even allowed by C++.
Some pointer comparisons might need to stay pointer comparison, but some will need to be changed into a virtual method call. I'll need to look at each one. What approaches are there to identify all these kinds of comparisons? Temporarily breaking either the build or execution is fine. There is pretty good test coverage.
I have read the question C++ Trick to avoid pointer comparison which is similar, but more limited because the accepted answer assumes the existence of a factory class.
Upvotes: 3
Views: 257
Reputation: 9343
You could write a custom code analysis tool. Here's a minimal (and rather trivial) example I've built using libclang
. This filters out every binary operator in the source. By the means of refining this, you could gather all pointer equality comparisons from the 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've 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 which my tool printed this:
Comparison: f == f
Comparison: b == b
Comparison: f == b
Upvotes: 5