boost :: bind с нулевыми указателями на функции - PullRequest
7 голосов
/ 06 января 2010

Если указатель функции, встроенный в возвращаемый объект boost::bind, равен NULL / nullptr / 0, мне нужно выполнить действие, отличное от его вызова. Как я могу определить, содержит ли объект нулевой указатель на функцию?

1007 * Addenda * Не думаю, что смогу использовать и сравнить boost::function s, поскольку возвращаемый объект boost::bind используется с различными сигнатурами вызовов в шаблонной функции. Упрощенный пример: 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); Решение

Поскольку арность функции в вызываемом объекте не изменяется, я могу «привести» объект возврата bind в boost::function и вызвать .empty()

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

Ответы [ 4 ]

5 голосов
/ 06 января 2010

Вы можете привязать к фиктивной функции:

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

... или, предполагая, что вы используете Boost.Bind вместе с Boost.Function, верните созданный по умолчанию объект функции и проверьте на empty() перед его вызовом:

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

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

По поводу обновления:
Я до сих пор не понимаю, в чем проблема с привязкой к другой функции, подобной следующей:

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));
}

Если вы хотите убрать эту обработку из вызывающего кода, вы можете эмулировать функцию шаблона переменной для поддержки переменных арностей:

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));

... или вы используете описанный выше подход для генерации boost::function<> объектов или ваших собственных оболочек и проверьте на functor.empty() или подобное в do_stuff().

1 голос
/ 11 января 2010

Я бы создал объект-оболочку для этого. Что-то вроде следующего

#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 ) );
}

Скомпилируйте это и запустите его без аргументов, и он устанавливает mightBeNullFnPtr в NULL и вызывает do_stuff с классом-оберткой, и выводит 7. Запустите его с аргументом, и он установит mightByNullFnPtr в aFunction и вызовет с этим do_stuff, напечатав из 30.

Если вам нужна большая универсальность, вам нужно создать шаблон класса-оболочки DefaultingFromFnPtr, но это должно быть довольно легко сделать.

0 голосов
/ 11 января 2010

Вам придется взломать boost.

boost :: bind возвращает unspecified-nn .Единственное, что можно сделать с этими классами, это оператор ().Единственное, что вы знаете, это то, что они являются копируемыми и имеют typedef для result_type (что, кстати, означает, что вам не нужен шаблон для типа результата).

Вы хотите что-то еще -поэтому вам нужно найти определение unspecified-nn в boost (их может быть несколько), взломать их, чтобы получить функцию-член is_null (), которая проверяет необходимые условия, а затем вызвать ее как вашtest.

Это, конечно, если вы уверены, что вы всегда получите объект boost :: bind'ed в своей функции шаблона.Если кто-то попытается передать обычный указатель на функцию, он не скомпилируется.Чтобы обойти это, потребуется немного магии шаблона.

0 голосов
/ 06 января 2010

Я почти уверен, что вызов boost :: bind с нулевым указателем (= создание объекта bind) следует рассматривать как неопределенное поведение, даже если сбой происходит только при вызове.

...