将std :: bind转换为函数指针

时间:2021-01-13 18:01:52

I have a third-party library which has a method that takes a function pointer as the first parameter:

我有一个第三方库,它有一个方法,它将一个函数指针作为第一个参数:

int third_party_method(void (*func)(double*, double*, int, int, double*), ...);

I want to pass a pointer to a class' method that is declared as follows:

我想传递一个指向声明如下的类方法的指针:

class TestClass
{
    public:
        void myFunction (double*, double*, int, int, void*);

I tried to pass this function as follows:

我试图传递这个函数如下:

TestClass* tc = new TestClass();
using namespace std::placeholders;
third_party_method(std::bind(&TestClass::myFunction, tc, _1, _2, _3, _4, _5), ...);

However, this does not compile:

但是,这不编译:

Conversion of parameter 1 from 'std::tr1::_Bind<_Result_type,_Ret,_BindN>' to 'void (__cdecl *)(double *,double *,int,int,void *)' is not possible
with
[
    _Result_type=void,
    _Ret=void,
    _BindN=std::tr1::_Bind6<std::tr1::_Callable_pmf<void (__thiscall TestClass::* const )(double *,double *,int,int,void *),TestClass,false>,TestClass *,std::tr1::_Ph<1>,std::tr1::_Ph<2>,std::tr1::_Ph<3>,std::tr1::_Ph<4>,std::tr1::_Ph<5>>
]

Is there any way I can pass the member to the function?

有什么方法可以将成员传递给函数吗?

4 个解决方案

#1


17  

Is there any way I can pass the member to the function?

有什么方法可以将成员传递给函数吗?

Unless your class object is some kind of global object - it is not possible. Because objects may contain some data, while function pointer is just pointer to function - it doesn't contain any runtime context, only compile-time one.

除非你的类对象是某种全局对象 - 否则它是不可能的。因为对象可能包含一些数据,而函数指针只是指向函数的指针 - 它不包含任何运行时上下文,只包含编译时的上下文。

If you accept having compile-time unique IDs for each callback passing, then you can use following generalized approach.

如果您接受每个回调传递的编译时唯一ID,那么您可以使用以下通用方法。

Usage:

用法:

void test(void (*fptr)())
{
    fptr();
}

struct SomeStruct
{
    int data;
    void some_method()
    {
        cout << data << endl;
    }
    void another_method()
    {
        cout << -data << endl;
    }
};

int main()
{
    SomeStruct local[] = { {11}, {22}, {33} };

    test(get_wrapper<0>(  boost::bind(&SomeStruct::some_method,local[0]) ));
    test(get_wrapper<1>(  boost::bind(&SomeStruct::another_method,local[0]) ));

    test(get_wrapper<2>(  boost::bind(&SomeStruct::some_method,local[1]) ));
    test(get_wrapper<3>(  boost::bind(&SomeStruct::another_method,local[1]) ));

    test(get_wrapper<4>(  boost::bind(&SomeStruct::some_method,local[2]) ));
    test(get_wrapper<5>(  boost::bind(&SomeStruct::another_method,local[2]) ));
}

It may not require Unique ID's for each invocation, for instance because Functors may already have different types, or runtime scope of their usage do not overlap. But it is safer to use unique ID each time.

每次调用可能不需要唯一ID,例如,因为Functors可能已经有不同的类型,或者它们的使用的运行时范围不重叠。但每次使用唯一ID更安全。

Implementation:

执行:

live demo

现场演示

#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <ostream>
using namespace std;

template<unsigned ID,typename Functor>
boost::optional<Functor> &get_local()
{
    static boost::optional<Functor> local;
    return local;
}

template<unsigned ID,typename Functor>
typename Functor::result_type wrapper()
{
    return get_local<ID,Functor>().get()();
}

template<typename ReturnType>
struct Func
{
    typedef ReturnType (*type)();
};

template<unsigned ID,typename Functor>
typename Func<typename Functor::result_type>::type get_wrapper(Functor f)
{
    (get_local<ID,Functor>()) = f;
    return wrapper<ID,Functor>;
}

// ----------------------------------------------------------------------

void test(void (*fptr)())
{
    fptr();
}

struct SomeStruct
{
    int data;
    void some_method()
    {
        cout << data << endl;
    }
    void another_method()
    {
        cout << -data << endl;
    }
};

int main()
{
    SomeStruct local[] = { {11}, {22}, {33} };

    test(get_wrapper<0>(  boost::bind(&SomeStruct::some_method,local[0]) ));
    test(get_wrapper<1>(  boost::bind(&SomeStruct::another_method,local[0]) ));

    test(get_wrapper<2>(  boost::bind(&SomeStruct::some_method,local[1]) ));
    test(get_wrapper<3>(  boost::bind(&SomeStruct::another_method,local[1]) ));

    test(get_wrapper<4>(  boost::bind(&SomeStruct::some_method,local[2]) ));
    test(get_wrapper<5>(  boost::bind(&SomeStruct::another_method,local[2]) ));
}

P.S. Beaware of multi-thread access - in such cases you should use some kind of Thread-local storage data.

附:要注意多线程访问 - 在这种情况下,您应该使用某种线程本地存储数据。

#2


5  

It doesn't compile because the third-party function is expecting a pointer-to-function, but you are trying to pass it a pointer-to-member-function. The two types are fundamentally different, and cannot be interchanged. In fact, pointers-to-member-functions are very often strange animals.

它不会编译,因为第三方函数期望指向函数的指针,但是您尝试将指针传递给成员函数。这两种类型根本不同,不能互换。实际上,指向成员函数的指针通常是奇怪的动物。

Here's an SSCCE illustrating the problem you're having:

这是一个SSCCE,说明您遇到的问题:

#include <iostream>
#include <iomanip>
using namespace std;

typedef void(*SpeakFn)(void);

void Bark()
{
    cout << "WOOF" << endl;
}

void Meow()
{
    cout << "meeow" << endl;
}

void SpeakUsing(SpeakFn fn)
{
    fn();
}

class Alligator
{
public:
    void Speak()
    {
        cout << "YAWWW" << endl;
    }
    typedef void(Alligator::*AlligatorSpeakFn)(void);

    void SpeakUsing(AlligatorSpeakFn fn)
    {
        (this->*fn)();
    }
};

int main()
{
    SpeakUsing(&Bark); // OK

    Alligator a;
    Alligator::AlligatorSpeakFn mem_fn = &Alligator::Speak;
    a.SpeakUsing(mem_fn);   // OK

    SpeakUsing(mem_fn); // NOT OK -- can't cvt from fn-ptr to mem-fn-ptr
}

You can't call SpeakUsing with a pointer-to-member-function because it's not convertible to pointer-to-function.

您不能使用指向成员函数的指针调用SpeakUsing,因为它不能转换为指向函数的指针。

Use a static member function instead, such as:

请改用静态成员函数,例如:

class Alligator
{
public:
    static void Speak()
    {
        cout << "YAWWW" << endl;
    }
    typedef void(*AlligatorSpeakFn)(void);

    void SpeakUsing(AlligatorSpeakFn fn)
    {
        fn();
    }
};

#3


4  

As other people mentioned you have no choice but to use global or static data to provide bind call context as raw function. But provided solution is not general, it's stuck with empty parameter list of functor. You will need to write manually wrapper, get_wrapper and Func for each different function signature you want to bind and give them different names.

正如其他人所提到的,你别无选择,只能使用全局或静态数据来提供绑定调用上下文作为原始函数。但是如果提供的解决方案不是通用的,那么它就会陷入仿函数的空参数列表。您需要为要绑定的每个不同的函数签名手动编写包装器,get_wrapper和Func,并为它们指定不同的名称。

I would like to propose more generalized solution for raw bind:

我想提出更广泛的原始绑定解决方案:

#include <iostream>
#include <memory>
#include <functional>
#include <cassert>

// Raw Bind - simulating auto storage behavior for static storage data
template <typename BindFunctor, typename FuncWrapper> class scoped_raw_bind
{
public:

   typedef scoped_raw_bind<BindFunctor, FuncWrapper> this_type;

   // Make it Move-Constructible only
   scoped_raw_bind(const this_type&) = delete;
   this_type& operator=(const this_type&) = delete;
   this_type& operator=(this_type&& rhs) = delete;

   scoped_raw_bind(this_type&& rhs): m_owning(rhs.m_owning)
   {
      rhs.m_owning = false;
   }

   scoped_raw_bind(BindFunctor b): m_owning(false)
   {
      // Precondition - check that we don't override static data for another raw bind instance
      if(get_bind_ptr() != nullptr)
      {
        assert(false);
        return;
      }
      // Smart pointer is required because bind expression is copy-constructible but not copy-assignable
      get_bind_ptr().reset(new BindFunctor(b));
      m_owning = true;
   }

   ~scoped_raw_bind()
   {
     if(m_owning)
     {
        assert(get_bind_ptr() != nullptr);
        get_bind_ptr().reset();
     }
   }

   decltype(&FuncWrapper::call) get_raw_ptr()
   {
      return &FuncWrapper::call;
   }

   static BindFunctor& get_bind()
   { 
      return *get_bind_ptr();
   }

private:

  bool m_owning;

  static std::unique_ptr<BindFunctor>& get_bind_ptr()
  {
     static std::unique_ptr<BindFunctor> s_funcPtr;
     return s_funcPtr;
  }

};

// Handy macro for creating raw bind object
// W is target function wrapper, B is source bind expression
#define RAW_BIND(W,B) std::move(scoped_raw_bind<decltype(B), W<decltype(B), __COUNTER__>>(B));

// Usage
///////////////////////////////////////////////////////////////////////////

// Target raw function signature
typedef void (*TargetFuncPtr)(double, int, const char*);

// Function that need to be called via bind
void f(double d, int i, const char* s1, const char* s2)
{
   std::cout << "f(" << d << ", " << i << ", " << s1 << ", " << s2 << ")" << std::endl;
}

// Wrapper for bound function
// id is required to generate unique type with static data for
// each raw bind instantiation.
// THE ONLY THING THAT YOU NEED TO WRITE MANUALLY!
template <typename BindFunc, int id = 0> struct fWrapper
{
   static void call(double d, int i, const char* s)
   {
      scoped_raw_bind<BindFunc, fWrapper<BindFunc, id>>::get_bind()(d, i, s);
   }
};

int main()
{
   using namespace std::placeholders;

   auto rf1 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 1"));
   TargetFuncPtr f1 = rf1.get_raw_ptr();
   f1(1.2345, 42, "f1: Bind! Bind!");

   auto rf2 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 2"));
   TargetFuncPtr f2 = rf2.get_raw_ptr();
   f2(10.2345, 420, "f2: Bind! Bind!");

   auto rf3 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 3"));
   TargetFuncPtr f3 = rf3.get_raw_ptr();
   f3(100.2345, 4200, "f3: Bind! Bind!");
}

It was tested - see Live Action Here

它经过测试 - 参见Live Action Here

#4


1  

No, not easily. The problem is that a function pointer has one chunk of info - the address of the function. A method needs both that and the address of the object, or alternatively it could be passed the objects address as a parameter.

不,不容易。问题是函数指针有一个信息块 - 函数的地址。方法既需要对象的地址,也需要对象的地址,或者可以将对象地址作为参数传递。

There are extremely hackey ways to do this, but they are going to be platform specific. And extremely hackey. So much so that I will recommend global variables instead of using them.

有非常hackey的方法来做到这一点,但它们将是特定于平台的。非常hackey。这么多,我将推荐全局变量而不是使用它们。

You do know how to do this if there is a single global instance of the class, right?

如果有一个类的全局实例,你知道如何做到这一点,对吗?

#1


17  

Is there any way I can pass the member to the function?

有什么方法可以将成员传递给函数吗?

Unless your class object is some kind of global object - it is not possible. Because objects may contain some data, while function pointer is just pointer to function - it doesn't contain any runtime context, only compile-time one.

除非你的类对象是某种全局对象 - 否则它是不可能的。因为对象可能包含一些数据,而函数指针只是指向函数的指针 - 它不包含任何运行时上下文,只包含编译时的上下文。

If you accept having compile-time unique IDs for each callback passing, then you can use following generalized approach.

如果您接受每个回调传递的编译时唯一ID,那么您可以使用以下通用方法。

Usage:

用法:

void test(void (*fptr)())
{
    fptr();
}

struct SomeStruct
{
    int data;
    void some_method()
    {
        cout << data << endl;
    }
    void another_method()
    {
        cout << -data << endl;
    }
};

int main()
{
    SomeStruct local[] = { {11}, {22}, {33} };

    test(get_wrapper<0>(  boost::bind(&SomeStruct::some_method,local[0]) ));
    test(get_wrapper<1>(  boost::bind(&SomeStruct::another_method,local[0]) ));

    test(get_wrapper<2>(  boost::bind(&SomeStruct::some_method,local[1]) ));
    test(get_wrapper<3>(  boost::bind(&SomeStruct::another_method,local[1]) ));

    test(get_wrapper<4>(  boost::bind(&SomeStruct::some_method,local[2]) ));
    test(get_wrapper<5>(  boost::bind(&SomeStruct::another_method,local[2]) ));
}

It may not require Unique ID's for each invocation, for instance because Functors may already have different types, or runtime scope of their usage do not overlap. But it is safer to use unique ID each time.

每次调用可能不需要唯一ID,例如,因为Functors可能已经有不同的类型,或者它们的使用的运行时范围不重叠。但每次使用唯一ID更安全。

Implementation:

执行:

live demo

现场演示

#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <ostream>
using namespace std;

template<unsigned ID,typename Functor>
boost::optional<Functor> &get_local()
{
    static boost::optional<Functor> local;
    return local;
}

template<unsigned ID,typename Functor>
typename Functor::result_type wrapper()
{
    return get_local<ID,Functor>().get()();
}

template<typename ReturnType>
struct Func
{
    typedef ReturnType (*type)();
};

template<unsigned ID,typename Functor>
typename Func<typename Functor::result_type>::type get_wrapper(Functor f)
{
    (get_local<ID,Functor>()) = f;
    return wrapper<ID,Functor>;
}

// ----------------------------------------------------------------------

void test(void (*fptr)())
{
    fptr();
}

struct SomeStruct
{
    int data;
    void some_method()
    {
        cout << data << endl;
    }
    void another_method()
    {
        cout << -data << endl;
    }
};

int main()
{
    SomeStruct local[] = { {11}, {22}, {33} };

    test(get_wrapper<0>(  boost::bind(&SomeStruct::some_method,local[0]) ));
    test(get_wrapper<1>(  boost::bind(&SomeStruct::another_method,local[0]) ));

    test(get_wrapper<2>(  boost::bind(&SomeStruct::some_method,local[1]) ));
    test(get_wrapper<3>(  boost::bind(&SomeStruct::another_method,local[1]) ));

    test(get_wrapper<4>(  boost::bind(&SomeStruct::some_method,local[2]) ));
    test(get_wrapper<5>(  boost::bind(&SomeStruct::another_method,local[2]) ));
}

P.S. Beaware of multi-thread access - in such cases you should use some kind of Thread-local storage data.

附:要注意多线程访问 - 在这种情况下,您应该使用某种线程本地存储数据。

#2


5  

It doesn't compile because the third-party function is expecting a pointer-to-function, but you are trying to pass it a pointer-to-member-function. The two types are fundamentally different, and cannot be interchanged. In fact, pointers-to-member-functions are very often strange animals.

它不会编译,因为第三方函数期望指向函数的指针,但是您尝试将指针传递给成员函数。这两种类型根本不同,不能互换。实际上,指向成员函数的指针通常是奇怪的动物。

Here's an SSCCE illustrating the problem you're having:

这是一个SSCCE,说明您遇到的问题:

#include <iostream>
#include <iomanip>
using namespace std;

typedef void(*SpeakFn)(void);

void Bark()
{
    cout << "WOOF" << endl;
}

void Meow()
{
    cout << "meeow" << endl;
}

void SpeakUsing(SpeakFn fn)
{
    fn();
}

class Alligator
{
public:
    void Speak()
    {
        cout << "YAWWW" << endl;
    }
    typedef void(Alligator::*AlligatorSpeakFn)(void);

    void SpeakUsing(AlligatorSpeakFn fn)
    {
        (this->*fn)();
    }
};

int main()
{
    SpeakUsing(&Bark); // OK

    Alligator a;
    Alligator::AlligatorSpeakFn mem_fn = &Alligator::Speak;
    a.SpeakUsing(mem_fn);   // OK

    SpeakUsing(mem_fn); // NOT OK -- can't cvt from fn-ptr to mem-fn-ptr
}

You can't call SpeakUsing with a pointer-to-member-function because it's not convertible to pointer-to-function.

您不能使用指向成员函数的指针调用SpeakUsing,因为它不能转换为指向函数的指针。

Use a static member function instead, such as:

请改用静态成员函数,例如:

class Alligator
{
public:
    static void Speak()
    {
        cout << "YAWWW" << endl;
    }
    typedef void(*AlligatorSpeakFn)(void);

    void SpeakUsing(AlligatorSpeakFn fn)
    {
        fn();
    }
};

#3


4  

As other people mentioned you have no choice but to use global or static data to provide bind call context as raw function. But provided solution is not general, it's stuck with empty parameter list of functor. You will need to write manually wrapper, get_wrapper and Func for each different function signature you want to bind and give them different names.

正如其他人所提到的,你别无选择,只能使用全局或静态数据来提供绑定调用上下文作为原始函数。但是如果提供的解决方案不是通用的,那么它就会陷入仿函数的空参数列表。您需要为要绑定的每个不同的函数签名手动编写包装器,get_wrapper和Func,并为它们指定不同的名称。

I would like to propose more generalized solution for raw bind:

我想提出更广泛的原始绑定解决方案:

#include <iostream>
#include <memory>
#include <functional>
#include <cassert>

// Raw Bind - simulating auto storage behavior for static storage data
template <typename BindFunctor, typename FuncWrapper> class scoped_raw_bind
{
public:

   typedef scoped_raw_bind<BindFunctor, FuncWrapper> this_type;

   // Make it Move-Constructible only
   scoped_raw_bind(const this_type&) = delete;
   this_type& operator=(const this_type&) = delete;
   this_type& operator=(this_type&& rhs) = delete;

   scoped_raw_bind(this_type&& rhs): m_owning(rhs.m_owning)
   {
      rhs.m_owning = false;
   }

   scoped_raw_bind(BindFunctor b): m_owning(false)
   {
      // Precondition - check that we don't override static data for another raw bind instance
      if(get_bind_ptr() != nullptr)
      {
        assert(false);
        return;
      }
      // Smart pointer is required because bind expression is copy-constructible but not copy-assignable
      get_bind_ptr().reset(new BindFunctor(b));
      m_owning = true;
   }

   ~scoped_raw_bind()
   {
     if(m_owning)
     {
        assert(get_bind_ptr() != nullptr);
        get_bind_ptr().reset();
     }
   }

   decltype(&FuncWrapper::call) get_raw_ptr()
   {
      return &FuncWrapper::call;
   }

   static BindFunctor& get_bind()
   { 
      return *get_bind_ptr();
   }

private:

  bool m_owning;

  static std::unique_ptr<BindFunctor>& get_bind_ptr()
  {
     static std::unique_ptr<BindFunctor> s_funcPtr;
     return s_funcPtr;
  }

};

// Handy macro for creating raw bind object
// W is target function wrapper, B is source bind expression
#define RAW_BIND(W,B) std::move(scoped_raw_bind<decltype(B), W<decltype(B), __COUNTER__>>(B));

// Usage
///////////////////////////////////////////////////////////////////////////

// Target raw function signature
typedef void (*TargetFuncPtr)(double, int, const char*);

// Function that need to be called via bind
void f(double d, int i, const char* s1, const char* s2)
{
   std::cout << "f(" << d << ", " << i << ", " << s1 << ", " << s2 << ")" << std::endl;
}

// Wrapper for bound function
// id is required to generate unique type with static data for
// each raw bind instantiation.
// THE ONLY THING THAT YOU NEED TO WRITE MANUALLY!
template <typename BindFunc, int id = 0> struct fWrapper
{
   static void call(double d, int i, const char* s)
   {
      scoped_raw_bind<BindFunc, fWrapper<BindFunc, id>>::get_bind()(d, i, s);
   }
};

int main()
{
   using namespace std::placeholders;

   auto rf1 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 1"));
   TargetFuncPtr f1 = rf1.get_raw_ptr();
   f1(1.2345, 42, "f1: Bind! Bind!");

   auto rf2 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 2"));
   TargetFuncPtr f2 = rf2.get_raw_ptr();
   f2(10.2345, 420, "f2: Bind! Bind!");

   auto rf3 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 3"));
   TargetFuncPtr f3 = rf3.get_raw_ptr();
   f3(100.2345, 4200, "f3: Bind! Bind!");
}

It was tested - see Live Action Here

它经过测试 - 参见Live Action Here

#4


1  

No, not easily. The problem is that a function pointer has one chunk of info - the address of the function. A method needs both that and the address of the object, or alternatively it could be passed the objects address as a parameter.

不,不容易。问题是函数指针有一个信息块 - 函数的地址。方法既需要对象的地址,也需要对象的地址,或者可以将对象地址作为参数传递。

There are extremely hackey ways to do this, but they are going to be platform specific. And extremely hackey. So much so that I will recommend global variables instead of using them.

有非常hackey的方法来做到这一点,但它们将是特定于平台的。非常hackey。这么多,我将推荐全局变量而不是使用它们。

You do know how to do this if there is a single global instance of the class, right?

如果有一个类的全局实例,你知道如何做到这一点,对吗?