views:

681

answers:

4

If the function pointer embedded in a boost::bind return object is NULL/nullptr/0, I need to take action other than calling it. How can I determine if the object contains a null function pointer?

Addenda

  1. I don't believe I can use and compare boost::functions as the boost::bind return object is used with varying call signatures in a template function.
  2. Simplified example:
template <typename BRO>
Retval do_stuff(BRO func, enum Fallback fallback)
{
    if (func == NULL)
    {
        return do_fallback(fallback);
    }
    else
    {
        return use_retval(func());
    }
}

do_stuff(boost::bind(FuncPtrThatMightBeNull, var1, var2), fallback);

Solution

Since the arity of the function in the callee does not change, I can "cast" the bind return object into a boost::function and call .empty()

Retval do_stuff(boost::function<Retval()> func, enum Fallback fallback)
{
    if (func.empty())
        return do_fallback(fallback);
    else
        return use_retval(func());
}
A: 

I'm pretty sure calling boost::bind with a null pointer (= the creation of the bind object) should be considered undefined behavior, even if the crash only happens when calling it.

Nicolás
yes but it's not.
Matt Joiner
+5  A: 

You can either bind to a dummy function:

void dummy() { /* has differing behaviour */ }
// ...
boost::bind(&dummy)();

... or, assuming you're using Boost.Bind together with Boost.Function, return a default constructed function object and check for empty() before calling it:

typedef boost::function<void (void)> F;
F create() { return F(); }

void use() {
    F f = create();
    if(f.empty()) {
        /* ... */
    }
}

Regarding the update:
I still don't see what the problem with binding to a different function like the following would be:

template <typename BRO>
Retval do_stuff(BRO func)
{
    return func();
}

if(funcPtr) {
    do_stuff(boost::bind(&use_retval, boost::bind(funcPtr, a, b)));
} else {
    do_stuff(boost::bind(&do_fallback, fallback));
}

If you'd want to move that handling out of the calling code, you could emulate variadic template function to support variable arities:

template<class R, class T1> 
boost::function<R (T1)> 
bind_wrap(R (*fnPtr)(), T1& t1, Fallback fallback) {
    if(fnPtr) return boost::bind(&use_retval,  boost::bind(funcPtr, t1));
    else      return boost::bind(&do_fallback, fallback);
}

template<class R, class T1, class T2> 
boost::function<R (T1, T2)> 
bind_wrap(R (*fnPtr)(T1, T2), T1& t1, T2& t2, Fallback fallback) {
    if(fnPtr) return boost::bind(&use_retval,  boost::bind(funcPtr, t1, t2));
    else      return boost::bind(&do_fallback, fallback);
}

// ... etc. for all needed arities

do_stuff(bind_wrap(funcPtr, var1, var2, fallback));

... or you use the approach above to generate boost::function<> objects or your own wrappers and check for functor.empty() or similar in do_stuff().

Georg Fritzsche
I'm not doing either of these. I can't bind to a dummy function as the "default action" I speak of is decided only after the function pointer is found to be null. The function signature varies, the return object from boost::bind is passed to a template function.
Matt Joiner
I just simplified it above. But what exactly is preventing you from using `functionObject.empty()` ... ? Maybe you can show a simplified example of your use-case?
Georg Fritzsche
The implementation of the returned object is by the way mainly in `boost/bind/bind_template.hpp` - there you can see that the function is stored privately and no accessors are provided - it is supposed to be opaque. I think the approach you are taking is flawed, you have to create your function objects somewhere - thats where you should fix it.
Georg Fritzsche
this answer is shaping up nicely. i should try out your ideas shortly
Matt Joiner
So it turns out that, seeing as I was packing the bind *before* passing it in, the expected call signature did not change. It was simply a `boost::function<RetVal()>`, which let me use the `.empty()` method. This answer gave me the right ideas and put me on the right track.
Matt Joiner
A: 

You're going to have to hack boost.

boost::bind returns unspecified-n-n. The only thing valid to do with these classes is operator(). The only other thing you know is that they are copy constructable, and have a typedef for result_type (which, by the way, means you don't need a template for result type).

You want something else - so you'll need to find the definition of unspecified-n-n in boost (there maybe several), hack them to have a is_null() member function which checks for the conditions you want, then call that as your test.

This is, of course, assuming you are certain you'll always get a boost::bind'ed object in your template function. If someone tries passing in a regular function pointer, it won't compile. Working around this will require some template magic.

Terry Mahaffey
A: 

I'd create a wrapper object to do this. Something like the following

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <iostream>

int aFunction(int i, int j)
{
  std::cout<<"In a Function"<<std::endl;
  return i+j;
}

struct DefaultingFromFnPtr : public boost::function< int(int,int) >
{
  explicit DefaultingFromFnPtr( int(*fn)(int,int) ) : fn_(fn) {}
  int operator()(int i, int j) const
  {
    if (fn_!=NULL) return fn_(i, j);
    return 7;
  }
  int(*fn_)(int,int);
};

template<typename T>
void do_stuff( T t )
{
  std::cout<<"RETURNED "<<t()<<std::endl;
}

int main( int argv, const char** argc)
{

  int(*mightBeNullFnPtr)(int,int) = NULL;
  if( argv>1)
  {
    mightBeNullFnPtr = & aFunction;
  }

  int var1 = 10;
  int var2 = 20;

  do_stuff( boost::bind( DefaultingFromFnPtr( mightBeNullFnPtr ), var1, var2 ) );
}

Compile this and run it with no arguments and it sets mightBeNullFnPtr to NULL and calls do_stuff with a wrapper class, and so prints out 7. Run it with an argument and it will set mightByNullFnPtr to aFunction and calls do_stuff with that, printing out 30.

If you want more genericity you will need to template the DefaultingFromFnPtr wrapper class, but that should be pretty easy to do.

Michael Anderson
Hm, you might be on to something. I'll give it a try.
Matt Joiner