|
Boost Users : |
From: Marc Horowitz (marc_at_[hidden])
Date: 2008-06-15 23:33:12
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.
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net