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.