Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defending against the danger of function name hiding class name

Tags:

c++

#include <iostream>
using namespace std;

#include "other_library.h"

struct Foo
{
    Foo() { cout << "class\n"; }
};

int main()
{
    Foo();  // or Foo() in any expression
}

This outputs class, or so we would think. The problem is that if other_library.h happens to have a function called Foo whose return type is suitable to appear in whatever expression we used Foo in then it silently changes the behaviour, e.g.:

int Foo() { cout << "func\n"; return 1; }

causes func to be output without any code changing in main. This is bad because of the possibility for insidious and hard-to-detect bugs ; even if it is not malicious intent on the part of other_library, a name clash could go undetected.


What is a good way to deal with this problem? It was originally raised by Dan Saks in 1997, and one suggested resolution is that all classes should be typedef'd:

typedef struct Foo Foo;

as the compiler does have to report a clash between a typedef-name and a function name. However this does not appear to be common practice - why not?

Clarification: this question is about good practices for our code to stop this undetected behaviour change happening without us noticing. (As opposed to how to solve it once we have realized that it is happening, which is easier -- e.g. rename our class).

like image 791
M.M Avatar asked Feb 15 '15 23:02

M.M


3 Answers

typedef struct Foo Foo;

as the compiler does have to report a clash between a typedef-name and a function name. However this does not appear to be common practice - why not?

I hold this truth to be self-evident: cumbersome code is cumbersome. You can actually rather easily fix this by working in a namespace of your own (which, assuming the header file is C++, too, the other_library should also do.

like image 144
Marcus Müller Avatar answered Nov 20 '22 23:11

Marcus Müller


Unfortunately, you cannot avoid this. From C++11 standard (3.3.10):

A class name (9.1) or enumeration name (7.2) can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.

The only way to guard against it is the typedef trick you pointed out (thanks for that!), using namespaces, or adhering to naming conventions (the latter not helping when dealing with 3rd party code). You could also try to wrap the include in a namespace (shown bellow), but as the comments pointed out, this could cause link errors in certain situations.

namespace otherlib {
#include "other_library.h"
}

Then:

otherlib::Foo();
like image 28
Rado Avatar answered Nov 20 '22 21:11

Rado


current best practises protect against this already

  • Naming Conventions, such as
    void camelCase() vs class PascalCase

  • namespaces

  • wrapper libs for everything not modern C++

like image 1
sp2danny Avatar answered Nov 20 '22 23:11

sp2danny