3

In the following code, the assertion is not raised with the Visual Studio compiler, but it is raised when compiling for the iPhone using XCode:

class X
{
public:

 virtual void A() {}
};

X x;

void main()
{
 // Define a valid member function pointer to X::A.
 void (X::*p)() = &X::A;

 assert(p != 0);
}

Is this a bug in the compiler? How else can one check for a null pointer in this case?

J-16 SDiZ
  • 26,473
  • 4
  • 65
  • 84
Kovsa
  • 31
  • 1
  • http://stackoverflow.com/questions/2000977/iphone-assert-just-terminates-the-program – Mahesh Jan 12 '11 at 06:12
  • @Mahesh: I don't think that answers his question. He isn't saying that the `assert` is causing his program to terminate when compiling for the iPhone. He's asking why the assertion isn't raised under Visual Studio. – Cody Gray - on strike Jan 12 '11 at 06:15
  • 1
    @Mahesh: The problem isn't in the behavior of assert, it's that the expression being tested is evaluated wrongly by the compiler. `p != 0` must evaluate to true according to the standard. – Ben Voigt Jan 12 '11 at 06:16
  • 4
    `main` never ever returns `void`, always `int`. – GManNickG Jan 12 '11 at 06:17
  • Maybe a stupid question - you're not compiling as Release, are you? Assert might be ifdef'd out if you are. – Merlyn Morgan-Graham Jan 12 '11 at 06:47
  • 1
    @Merlyn: `assert(true)` is a no-op in both debug and release modes. The expression inside is true, guaranteed by the standard. – Ben Voigt Jan 12 '11 at 06:52
  • @Ben: Yes, I meant `assert(false)` might change. But you're right, it should be true. I think I made the mistake when I read, "is this a bug in the compiler," by making an assumption that VS got it wrong. – Merlyn Morgan-Graham Jan 12 '11 at 09:09

2 Answers2

10

The code is correct, the compiler is out of compliance with the standard, which says (section [expr.eq], using the wording from C++0x draft n3225, but it should be the same in other versions):

any pointer to member can be compared to a null pointer constant

and

If both operands are null, they compare equal. Otherwise if only one is null, they compare unequal.

relevant definition of null pointer constant (section [conv.ptr], the std::nullptr_t part is new in C++0x):

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t.

and (section [expr.const]):

A constant expression is an integral constant expression if it is of integral or enumeration type.

NOTE: As an aside, the implementation-defined representation of pointer-to-virtual-member-function usually is an index into the virtual table, which would be 0 in the question. But according to the standard, the expression inside the assert isn't checking if the representation is zero, it's checking against a zero literal -- it is a null pointer check.

And the Apple compiler apparently mixed the two up. If you wanted to test if the representation is zero, you could write assert((intptr_t)p != 0) -- and that would be completely non-portable.

But the code as written is perfectly portable, to any standard-compliant compiler, and will never assert.

EDIT: And one more citation from the standard, which just repeats what we've already learned (section [conv.mem]):

A null pointer constant (4.10) can be converted to a pointer to member type; the result is the null member pointer value of that type and is distinguishable from any pointer to member not created from a null pointer constant.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • But no one said that `NULL` has to be zero... right? So why is that legal? – user541686 Jan 12 '11 at 06:19
  • 2
    NULL appears nowhere in the OPs code though, or in the quoted portions of the standard -- the standard does explicitly state that an integral constant that evaluates to zero (which was used in the OP's assert) will be converted to the null pointer constant, and that's what the standard is referring to here. Not the preprocessor macro NULL. –  Jan 12 '11 at 06:21
  • @Lambert: `NULL` expands to an *integral constant expression* that evaluates to 0; such an expression is called a *null pointer constant*. A null pointer constant can convert to any pointer type as the *null pointer value*, an implementation-defined pointer representation of "null" that is *distinct* from any other value (compares unequal). Two null pointer values compare equal. The previous is also true for member pointers: a null pointer constant can be converted to any member pointer type as the *null member pointer value*. – GManNickG Jan 12 '11 at 06:22
  • 1
    @GMan: I know that that's what effectively happens in every compiler, but is it actually defined in the C++ standard that `NULL == 0`? – user541686 Jan 12 '11 at 06:24
  • Not sure what the standard says, but Stroustrup's seminal book The C++ Programming Language says, in section 5.1.1, that NULL is 0. – kirakun Jan 12 '11 at 06:24
  • I think a pointer to virtual member have defined behaviour only when it is bounded – J-16 SDiZ Jan 12 '11 at 06:26
  • 1
    @Lambert: Yes, that's what everyone is trying to tell you. I've quoted the relevant definitions from the standard in my edited answer. – Ben Voigt Jan 12 '11 at 06:26
  • @J-16: The standard says "any pointer to member". "any" clearly includes pointer-to-members which are pointers to virtual member functions. – Ben Voigt Jan 12 '11 at 06:27
  • @Ben: But that's for C++0x, not C++. (I'm not saying it's not, I'm genuinely trying to make sure that the official C++ standard says this is true, because I read in a book that it doesn't have to be.) – user541686 Jan 12 '11 at 06:27
  • @Lambert It is defined that `NULL` and `0` are equal when casted to a pointer. And `NULL` is always compare equal to `0`. (it have some other gotcha, but they are unrelated to this issue) – J-16 SDiZ Jan 12 '11 at 06:27
  • @Lambert: literal `0` is a null pointer constant. What the `NULL` macro evaluates to (usually `0` in C++ and `((void*)0)` in C) is irrelevant to this question. The current ISO standard (commonly called C++03) isn't free, I choose to cite the one that is freely available to anyone who wants to study it. – Ben Voigt Jan 12 '11 at 06:28
  • @Ben: Huh, okay... but then, why does `NULL` even exist? Why not just `0`? – user541686 Jan 12 '11 at 06:29
  • @Lambert: What part of my comment are you confused about? My previous comment is absolutely factual within the C++03 standard. – GManNickG Jan 12 '11 at 06:30
  • 1
    @Lambert: The C version (e.g. `((void*)0)`) may act differently in a variable length argument list. But that's not portable, since `NULL` is often defined as simply `0`. Many C++ experts, including Stroustrup IIRC, do just use `0` and forego the `NULL` macro. – Ben Voigt Jan 12 '11 at 06:31
  • @GMan: It wasn't your comment, it was your post that said C++0x, so I just wanted to make sure it's also true for C++. – user541686 Jan 12 '11 at 06:31
  • @Ben: It's not that I haven't seen code that users `0` instead of `NULL`. But I'm just trying to differentiate between "all experts use it" and "it is guaranteed by the standard". – user541686 Jan 12 '11 at 06:33
  • 1
    @Lambert: GMan didn't say anything about C++0x. I did, and I also specifically told you where C++0x changed something (the new `std::nullptr_t` type). – Ben Voigt Jan 12 '11 at 06:33
  • @Ben: Huh, okay, funny my book was wrong. Do you happen to know if this was also true for in the C standard, or just C++? (Edit: @GMan: Sorry, I messed up you two when referring to the post.) – user541686 Jan 12 '11 at 06:34
  • @Lambert: `0` is guaranteed by the C++ standards to be a *null pointer literal*. `NULL` is not guaranteed, although by convention we make it the same. For C, it's `(any_pointer_type)0` that is defined by the standard to be a *null pointer literal*, that's why the `NULL` macro is defined differently in C vs. C++. – Ben Voigt Jan 12 '11 at 06:34
  • @Ben: (edited) So the C standard differs from C++ in this respect... interesting, I didn't know that. Thanks. – user541686 Jan 12 '11 at 06:35
  • 1
    @Lambert: [NULL is a symbolic constant](http://www.gnu.org/s/libc/manual/html_node/Null-Pointer-Constant.html). Why would you write `degrees = radians * 180 / PI;` instead of `degrees = radians * 57.295779513;` ? Because it makes the meaning clear to readers. – Ben Voigt Jan 12 '11 at 06:37
  • @Ben: (I edited my last comment) Well, I always imagined that it was because `NULL` was always guaranteed to be an invalid pointer, and it just *happened* to be zero for most implementations, not that it was guaranteed. – user541686 Jan 12 '11 at 06:38
  • @Lambert: And to add to that (@Ben's), over time many have found 0 to be clearer than `NULL`, especially since in C++ they evaluate to the same thing, and `NULL` doesn't actually have to do anything with pointers, per se. But since we *do* want to be able to specify the null pointer value directly, instead of through an integral constant expression (for overload resolution, for example), we introduced `nullptr`. – GManNickG Jan 12 '11 at 06:38
  • Thanks Ben for your confirmation. – Kovsa Jan 12 '11 at 06:41
  • I'm looking at the [C++ standard](http://www-d0.fnal.gov/~dladams/cxx_standard.pdf), and, doing a quick search for `NULL`, I can't find anywhere that it says that `NULL` has to be zero, just that `NULL` is implementation-defined. Can someone point me to the right page? – user541686 Jan 12 '11 at 06:42
  • @Lambert: The null pointer *representation* isn't guaranteed to be zero. e.g. `int z = 0; reinterpret_cast(z)` might not be a NULL pointer. It's only *integral constant expressions* (usually literals) that invoke this rule in the standard. The literal `0` is a standard way (guaranteed by the standard, that is) of using the implementation-specific null pointer representation is source code. – Ben Voigt Jan 12 '11 at 06:42
  • @Lambert: really? Three times in my answer I gave you not only the quote, but also the section name. Look at `[conv.ptr]`. Page 60 in your version. – Ben Voigt Jan 12 '11 at 06:44
  • @Ben: Ohh... so you're saying that if you say `p != 0`, it might actually translate to `p != 1234` down the line, if that's the null pointer? – user541686 Jan 12 '11 at 06:45
  • 1
    @Lambert: Almost. `p != 0` in C++ source code might become `CMP p, #1234` in the resulting machine code. – Ben Voigt Jan 12 '11 at 06:46
  • In fact, the implementation-defined representation of pointer-to-virtual-member-function usually is an index into the virtual table, which would be 0 in the question. But according to the standard, the expression inside the assert isn't checking if the representation is zero, it's checking against a zero literal -- that is a null pointer check. And the Apple compiler apparently mixed the two up. If you wanted to test if the representation is zero, you could write `assert((intptr_t)p != 0)` -- and that would be VERY non-portable – Ben Voigt Jan 12 '11 at 06:50
  • @Ben Voigt, c++ 1998 5.10 #2 ... Otherwise if either is a pointer to a virtual member function, the result is unspecified... – J-16 SDiZ Jan 12 '11 at 06:55
  • @J-16: Do you know what "otherwise" means? This case clearly falls into one of the first two rules, which I quoted in my answer already. Comparison with a null pointer never gets to the rule you cited. – Ben Voigt Jan 12 '11 at 06:57
3

The iPhone compiler gets it wrong.

Note that equality comparison between member function pointers that point to virtual functions produces unspecified result, i.e. this assertion

assert(&X::A == &X::A);

behaves unpredictably from the formal point of view.

But comparison of a member function pointers with null-pointer constant is strictly defined by the language. The pointer cannot be null in your example, so it shall not compare equal to a null-pointer constant.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765