By Yves


2019-07-10 02:23:24 8 Comments

I'm reading the code of ROS.

In the file ros_comm/roscpp/include/ros/subscriber.h, I see such a piece of code:

operator void*() const { return (impl_ && impl_->isValid()) ? (void*)1 : (void*)0; }

Well, (void *)0 can be regarded as NULL in C, but what does (void *)1 mean?

If a class Foo contains this function, it means that we can code like this:

Foo foo;
void *ptr = foo;

Right? So does it mean that void *ptr = (void *)1 is possible? What does this mean?

2 comments

@Damon 2019-07-10 17:33:14

It shows that either the person who wrote the code isn't very well acquainted with the language or tools they're using, or the code has been around for a long, long time and been hacked on by different people, presumably having undergone a C-to-C++ transition at some time in the past, still carrying some legacy API contract (expecting a void*) which may be troublesome to change.

There is no good reason to do such a thing, if you look at the source. impl_ is a boost::shared_ptr<Impl> which implements operator bool, and Impl::isValid returns bool, too. There's no reason to use, or return anything but bool anywhere.

Basically, this is a contorted (and possibly dangerous) way of writing:

return impl_ && impl_->isValid();

@Deduplicator 2019-07-10 18:24:11

@Damon 2019-07-11 14:18:01

@Deduplicator: It sure is older, but that's besides the point (the explicit keyword doesn't really make much of a difference here, none of the exposed API funcs makes use of it). The so-called safe bool idiom is a misnomer, though -- dirty hack would be better suited. It is by no means safe, or expresses intent well. A bool communicates: "this is a yes or no value" whereas a void* communicates "I am a pointer, please dereference me". Which, for (void*)0 or (void*)1usually isn't such a brilliant idea. The fact that it "works" when incidentially used differently is irrelevant.

@Damon 2019-07-11 14:21:21

Programming is not just about "works for me" or even "works, I believe", but it is about making sure that things are well-defined, well-expressed, unambiguous, giving reproducable results and being as fail-safe as you can provide. Someone calling that function must immediately, without consulting 20 pages of documentation, know what to do with the return value. That's not the case with a void*. The notion "yeah, but there's an implicit conversion to... whatever" isn't precisely good.

@Deduplicator 2019-07-11 15:01:06

Nobody reasonable would ever deny that the safe bool idiom is a dirty hack, with all that implies. Unfortunately, it is also the best that could be done pre-C++11. And I only referenced boost::shared_ptr's history because while you acknowledged that the code under discussion might be ancient, you curiously used the current incarnation of that library as an argument about the language version used.

@Damon 2019-07-11 15:48:10

@Deduplicator: I'll have to admit that I'm not well acquainted with Boost versions of the mid-late-1990s :-) Though I'm pretty sure that return !!m_ptr; was well-defined and working perfectly reliably (with the exception of absence of an explicit keyword, obviously) in the late 80s already. Beats me why, for the sake of preventing a mostly harmless possible and rarely occuring bool-to-int conversion, one would sacrifice clarity and expressivity.

@Deduplicator 2019-07-11 15:55:56

Well, it was perfectly clear in use. The return-type is just a monstrosity to write, and sometimes leaked into diagnostics. Also, overload-resolution was AFAICT the key scenario. Well, let's consign it to the dustbin.

@Miles Budnek 2019-07-10 02:44:38

This is an old trick to avoid problems with implicit conversions to bool from before explicit contextual conversions were introduced in C++11. It's intended to be used to check validity:

Subscriber my_subscriber = someFunction();
if (!my_subscriber) {
    // error case
}

The important point is that no built-in conversion exists from void* to integer types, but one does exist from bool to integer types. At the same time, a built-in conversion from void* to bool exists. That means that if you define an implicit conversion to bool, then the following is surprisingly valid:

void my_func(int i);

void another_func() {
    Subscriber sub = something();
    my_func(sub);
}

Defining a conversion to void* avoids that issue.


These days that trick is obsolete though. C++11 introduced explicit conversions. explicit conversions to bool are considered in the conditions of if and loops, but aren't considered in other problematic cases. That means that these days that conversion should be written as:

explicit operator bool() const { return impl_ && impl_->isValid(); }

@Deduplicator 2019-07-10 15:49:03

Though the return-type used in the safe bool Idiom is normally a pointer to member-function so there is even less chance of misuse.

Related Questions

Sponsored Content

11 Answered Questions

[SOLVED] What is move semantics?

27 Answered Questions

21 Answered Questions

[SOLVED] What is the "-->" operator in C++?

7 Answered Questions

4 Answered Questions

[SOLVED] What does T&& (double ampersand) mean in C++11?

34 Answered Questions

8 Answered Questions

[SOLVED] What is The Rule of Three?

11 Answered Questions

[SOLVED] What does the explicit keyword mean?

5 Answered Questions

[SOLVED] What is the copy-and-swap idiom?

Sponsored Content