7

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());
}
Matt Joiner
  • 112,946
  • 110
  • 377
  • 526

4 Answers4

6

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
  • 97,545
  • 26
  • 194
  • 236
  • 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 Jan 09 '10 at 14:47
  • 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 Jan 10 '10 at 01:03
  • 1
    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 Jan 10 '10 at 01:42
  • this answer is shaping up nicely. i should try out your ideas shortly – Matt Joiner Jan 13 '10 at 04:01
  • 1
    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`, which let me use the `.empty()` method. This answer gave me the right ideas and put me on the right track. – Matt Joiner Jan 15 '10 at 00:44
1

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
  • 70,661
  • 7
  • 134
  • 187
0

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
  • 7,423
  • 33
  • 35
-1

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
  • 11,775
  • 1
  • 35
  • 44