I am attempting to use Boost.Bind to create a callable where some of the bound arguments might be NULL. Here is a simple example: void f(const char *) {} int main() { (std::tr1::bind(f, _1))(NULL); return(0); } msvc complains: c:\Program Files\boost\boost_1_35_0\boost/bind.hpp(232) : error C2664: 'void (const char *)' : cannot convert parameter 1 from 'const int' to 'const char *' Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast c:\Program Files\boost\boost_1_35_0\boost/bind/bind_template.hpp(47) : see reference to function template instantiation 'void boost::_bi::list1<A1>::operator ()<void(__cdecl *)(const char *),boost::_bi::list1<const int &>>(boost::_bi::type<T>,F &,A &,int)' being compiled with [ A1=boost::arg<1>, T=void, F=void (__cdecl *)(const char *), A=boost::_bi::list1<const int &> ] foo.cc(20) : see reference to function template instantiation 'void boost::_bi::bind_t<R,F,L>::operator ()<int>(const A1 &)' being compiled with [ R=void, F=void (__cdecl *)(const char *), L=boost::_bi::list1<boost::arg<1>>, A1=int ] g++ complains similarly: /usr/local/boost_1_35_0/boost/bind.hpp: In member function `void boost::_bi::list1<A1>::operator()(boost::_bi::type<void>, F&, A&, int) [with F = void (*)(const char*), A = boost::_bi::list1<const int&>, A1 = boost::arg<1> (*)()]': /usr/local/boost_1_35_0/boost/bind/bind_template.hpp:47: instantiated from `typename boost::_bi::result_traits<R, F>::type boost::_bi::bind_t<R, F, L>::operator()(const A1&) [with A1 = int, R = void, F = void (*)(const char*), L = boost::_bi::list1<boost::arg<1> (*)()>]' foo.cc:20: instantiated from here /usr/local/boost_1_35_0/boost/bind.hpp:232: error: invalid conversion from `const int' to `const char*' Of course, f(NULL) works just fine. The problem seems to be that NULL expands to 0. As a literal constant, this is assignable to any pointer, but once all the template stuff happens, you end up with an expression with integer type, and that's different. Similarly (and more predictably), this fails, too: int z = 0; f(z); I have a workaround, which is to replace the constant NULL with an expression which is assignable to any pointer type. Fortunately, this is easy in C++: class NullPlaceholder { public: template <class T> operator T*() const { return((T*)NULL); } }; NullPlaceholder _NULL; Now, I can write (std::tr1::bind(f, _1))(_NULL); and the compiler is happy. Is there a better workaround for this problem? It seems like wanting to use NULL as a constant would be a pretty common case. Ideally, bind would be fixed so that NULL works, but I'm not sure how to do that without loosening the type checking for all int->pointer conversions. (I played around with trying to use specialization to add some scaffolding so that the A{1,2,3,...} template types for an int 0 literal would actually use a NullPlaceholder, but I didn't get very far, and while this would work for the compilers I use, it would not be formally portable.) A documentation note, at least, would have helped me figure out what was going wrong. Providing _NULL as a "placeholder" in the library would be the next best thing to having NULL work as is. I'm not sure if there are any other common expressions in C++ which will run into similar issues. Integer and string literal constants work just fine, but I may be missing some more subtle case. Marc P.S. It occurs to me that replacing the runtime's NULL with a macro which expanded to a constant of type NullPlaceholder should work perfectly (and does in my tests). Any use of NULL in an expression would dtrt, and optimize to the use of a constant. But overriding the runtime like this just feels sketchy.
Mark, in this case template deduction does not work (or works a bit different as assumed). This one should work: (std::tr1::bind(f, _1))(static_cast<const char*>(NULL)); NULL is a Macro which is defined as integral 0 and compiler can't just convert any integer to a char const* implicitly. You should specify this explicitly. NullPlaceholder works, because it makes the cast in operator() for you. Greetings, Ovanes On Mon, Jun 16, 2008 at 5:33 AM, Marc Horowitz <marc@mit.edu> wrote:
I am attempting to use Boost.Bind to create a callable where some of the bound arguments might be NULL. Here is a simple example:
void f(const char *) {}
int main() { (std::tr1::bind(f, _1))(NULL);
return(0); }
msvc complains:
c:\Program Files\boost\boost_1_35_0\boost/bind.hpp(232) : error C2664: 'void (const char *)' : cannot convert parameter 1 from 'const int' to 'const char *' Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast c:\Program Files\boost\boost_1_35_0\boost/bind/bind_template.hpp(47) : see reference to function template instantiation 'void boost::_bi::list1<A1>::operator ()<void(__cdecl *)(const char *),boost::_bi::list1<const int &>>(boost::_bi::type<T>,F &,A &,int)' being compiled with [ A1=boost::arg<1>, T=void, F=void (__cdecl *)(const char *), A=boost::_bi::list1<const int &> ] foo.cc(20) : see reference to function template instantiation 'void boost::_bi::bind_t<R,F,L>::operator ()<int>(const A1 &)' being compiled with [ R=void, F=void (__cdecl *)(const char *), L=boost::_bi::list1<boost::arg<1>>, A1=int ]
g++ complains similarly:
/usr/local/boost_1_35_0/boost/bind.hpp: In member function `void boost::_bi::list1<A1>::operator()(boost::_bi::type<void>, F&, A&, int) [with F = void (*)(const char*), A = boost::_bi::list1<const int&>, A1 = boost::arg<1> (*)()]': /usr/local/boost_1_35_0/boost/bind/bind_template.hpp:47: instantiated from `typename boost::_bi::result_traits<R, F>::type boost::_bi::bind_t<R, F, L>::operator()(const A1&) [with A1 = int, R = void, F = void (*)(const char*), L = boost::_bi::list1<boost::arg<1> (*)()>]' foo.cc:20: instantiated from here /usr/local/boost_1_35_0/boost/bind.hpp:232: error: invalid conversion from `const int' to `const char*'
Of course, f(NULL) works just fine.
The problem seems to be that NULL expands to 0. As a literal constant, this is assignable to any pointer, but once all the template stuff happens, you end up with an expression with integer type, and that's different. Similarly (and more predictably), this fails, too:
int z = 0; f(z);
I have a workaround, which is to replace the constant NULL with an expression which is assignable to any pointer type. Fortunately, this is easy in C++:
class NullPlaceholder { public: template <class T> operator T*() const { return((T*)NULL); } };
NullPlaceholder _NULL;
Now, I can write (std::tr1::bind(f, _1))(_NULL); and the compiler is happy.
Is there a better workaround for this problem? It seems like wanting to use NULL as a constant would be a pretty common case. Ideally, bind would be fixed so that NULL works, but I'm not sure how to do that without loosening the type checking for all int->pointer conversions. (I played around with trying to use specialization to add some scaffolding so that the A{1,2,3,...} template types for an int 0 literal would actually use a NullPlaceholder, but I didn't get very far, and while this would work for the compilers I use, it would not be formally portable.) A documentation note, at least, would have helped me figure out what was going wrong. Providing _NULL as a "placeholder" in the library would be the next best thing to having NULL work as is.
I'm not sure if there are any other common expressions in C++ which will run into similar issues. Integer and string literal constants work just fine, but I may be missing some more subtle case.
Marc
P.S. It occurs to me that replacing the runtime's NULL with a macro which expanded to a constant of type NullPlaceholder should work perfectly (and does in my tests). Any use of NULL in an expression would dtrt, and optimize to the use of a constant. But overriding the runtime like this just feels sketchy.
"Ovanes Markarian" <om_boost@keywallet.com> writes:
in this case template deduction does not work (or works a bit different as assumed).
This one should work: (std::tr1::bind(f, _1))(static_cast<const char*>(NULL));
This works, but it is inconvenient. It is not always clear at first what the type of an argument which takes NULL is, especially if one is modifying existing code. You can compile, see what the error message says, and copy that type into the code, but I was hoping to avoid this.
NullPlaceholder works, because it makes the cast in operator() for you.
Yes, I am aware of this, which is why I wrote it that way. I was hoping to learn if someone had developed a cleaner workaround, and to get the documentation improved if not. Marc
On Mon, Jun 16, 2008 at 10:45 AM, Marc Horowitz <marc@mit.edu> wrote:
"Ovanes Markarian" <om_boost@keywallet.com> writes:
in this case template deduction does not work (or works a bit different as assumed).
This one should work: (std::tr1::bind(f, _1))(static_cast<const char*>(NULL));
This works, but it is inconvenient. It is not always clear at first what the type of an argument which takes NULL is, especially if one is modifying existing code. You can compile, see what the error message says, and copy that type into the code, but I was hoping to avoid this.
This is static type safety of C++. The next standard will introduce null_ptr. If used as you suggested, then nothing prevents this BAD code to compile: (std::tr1::bind(f, _1))(1);
NullPlaceholder works, because it makes the cast in operator() for you.
Yes, I am aware of this, which is why I wrote it that way. I was hoping to learn if someone had developed a cleaner workaround, and to get the documentation improved if not.
I don't see it as a workaround, but a clean approach to initialize pointer values with NULL-value only. This approach cleanly prevents the upper error case as well.
Marc
Regards, Ovanes
On Mon, Jun 16, 2008 at 01:01:58PM +0200, Ovanes Markarian wrote:
On Mon, Jun 16, 2008 at 10:45 AM, Marc Horowitz <marc@mit.edu> wrote:
"Ovanes Markarian" <om_boost@keywallet.com> writes:
in this case template deduction does not work (or works a bit different as assumed).
This one should work: (std::tr1::bind(f, _1))(static_cast<const char*>(NULL));
This is static type safety of C++. The next standard will introduce null_ptr. If used as you suggested, then nothing prevents this BAD code to
Right, null_ptr will be part of C++. Nevertheless NULL was never part of C++! g++ defines it for convenience, the Intel compiler does not. It is often defined by 3rd libraries. glib uses: #ifndef NULL # ifdef __cplusplus # define NULL (0L) # else /* !__cplusplus */ # define NULL ((void*) 0) # endif /* !__cplusplus */ #endif /usr/include/linux/stddef.h is similar but uses 0 instead of 0L in C++ mode. Nevertheless even with nullptr as defined as in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf I wasn't able to let the Intel compiler warning warning #1881: argument must be a constant null pointer value vanish. The code I used is: XtVaCreateManagedWidget("widget_name", xmFrameWidgetClass, form, NULL); (where I replaced NULL by nullptr, ...) So don't rely on NULL. It's evil ... null_ptr doesn't help in all cases as well :-) Jens
Right, null_ptr will be part of C++. Nevertheless NULL was never part of C++! g++ defines it for convenience, the Intel compiler does not.
At least C++ Standard (ISO/IEC 14882:2003(E)) states: 18.1 Types [lib.support.types] Common definitions. Header <cstddef> (Table 15): [tabel with definitions follows...] The contents are the same as the Standard C library header <stddef.h>, with the following changes: The macro NULL is an implementation-defined C + + null pointer constant in this International Standard (4.10).180) 180) Possible definitions include 0 and 0L, but not (void*)0. C.2.2.3 Macro NULL [diff.null] The macro NULL, defined in any of <clocale>, <cstddef>, <cstdio>, <cstdlib>, <cstring>, <ctime>, or <cwchar>, is an implementation-defined C + + null pointer constant in this International Standard (18.1). And this is what Stroustrup states: http://www.research.att.com/~bs/bs_faq2.html#null Greetings, Ovanes
Hi Ovanes, On Mon, Jun 16, 2008 at 06:19:57PM +0200, Ovanes Markarian wrote:
Right, null_ptr will be part of C++. Nevertheless NULL was never part of C++! g++ defines it for convenience, the Intel compiler does not.
At least C++ Standard (ISO/IEC 14882:2003(E)) states:
thanks for this information. You noticed that I confused things :-) Changing "C++ doesn't define NULL" into "... as (void*)0" was more or less what I mean.
And this is what Stroustrup states: http://www.research.att.com/~bs/bs_faq2.html#null
Yep, I'm at least aware of this ... Jens
Jens Seidel <jensseidel@users.sourceforge.net> writes:
Right, null_ptr will be part of C++. Nevertheless NULL was never part of C++! g++ defines it for convenience, the Intel compiler does not.
Perhaps boost should provide it, if the compiler doesn't?
It is often defined by 3rd libraries. glib uses: #ifndef NULL # ifdef __cplusplus # define NULL (0L) # else /* !__cplusplus */ # define NULL ((void*) 0) # endif /* !__cplusplus */ #endif
/usr/include/linux/stddef.h is similar but uses 0 instead of 0L in C++ mode.
Nevertheless even with nullptr as defined as in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1601.pdf I wasn't able to let the Intel compiler warning warning #1881: argument must be a constant null pointer value vanish. The code I used is: XtVaCreateManagedWidget("widget_name", xmFrameWidgetClass, form, NULL); (where I replaced NULL by nullptr, ...)
So don't rely on NULL. It's evil ... null_ptr doesn't help in all cases as well :-)
Well, the actual environment here is some COM methods under msvc. C++ may not specify NULL, but the COM interface docs specify the use of it all over the place, which leaves me little choice. If this means you want to add COM to the evil list, I won't object :-) Marc
Marc Horowitz:
I am attempting to use Boost.Bind to create a callable where some of the bound arguments might be NULL. Here is a simple example:
void f(const char *) {}
int main() { (std::tr1::bind(f, _1))(NULL);
return(0); }
This is indeed annoying. I've also been using
class NullPlaceholder { public: template <class T> operator T*() const { return((T*)NULL); } };
NullPlaceholder _NULL;
except not calling it _NULL since this name is reserved. We should probably have boost/nullptr.hpp that defines the macro nullptr to something like (::boost::nullptr_t()) in C++03 mode and does nothing otherwise (as C++0x will have a nullptr keyword.) shared_ptr will also need a nullptr_t type when it's updated to match the C++0x interface.
participants (4)
-
Jens Seidel
-
Marc Horowitz
-
Ovanes Markarian
-
Peter Dimov