简体   繁体   中英

how can function object be used as a function pointer when the function's argument is function pointer

I have a question related to function objects or functors in C++. I understand that function objects can substitute function pointers in many cases, such as some algorithms in STL. However, I do not know how to invoke function object if the function's argument is function pointer. For example:

          class ClassA
        {
        public:
            void operator()(std::string &name)
            {
                std::cout<<"Class A"<<std::endl;
            }
        };

        class ClassB
        {
        public:
            void operator()(std::string &name)
            {
                std::cout<<"Class B"<<std::endl;
            }
        };

        void testClass(void(*fun)(std::string&), std::string &name  )
        {
             (*fun)(name);
        }

            void funClass(std::string &str)
            {
                std::cout<<"hello C "<<str<<std::endl;

                   }

It is very easy to invoke the testClass function if I only use function pointer:

std::string str= "hello.txt";


        testClass(funClass,str);

However, I donot know how to invoke testClass if I want to use function object.

   ClassA obj;
testClass(obj.operator()(str),str); // compilation errors

A "functor" class is not a function pointer. However std::function takes constructors that will implicitly create one from a functor or function pointer.

If you are allowed to modify testClass then ideally make it more generic:

void testClass( std::function< void( std::string & ) > func, std::string & name )
{
   func( name );
}

You can then call

testClass( ClassA(), name );

If it is not going to modify the string it should probably take this parameter by const reference.

You could write delegate functions for your classes that forward them like this but I don't think that's really what you are looking for as you'd have to write one for each delegate:

Otherwise you can write a templated function like this

template< typename T >
void callT( std::string & name )
{
    T t;
    t( name );
}


testClass( callT< ClassA > );

If you are using C++11 you can pass in a lambda function in your instance which will convert to a function pointer. (Thanks Steve Jessop for telling me this).

In this case it is a bit of a workaround as you would have to type out the lambda function for every functor class you want to use it with. (So even though the feature is there the template is possibly better).

To do it with the lambda you would do

testClass( []( std::string & str ){ ClassA a; a(str); }, name );

Note that for the lambda to be able to convert to a function it cannot take captchas, but then it wouldn't be possible to do the templated version if you needed extra data members either.

A class instance is in general not a function pointer, but a class instance that is a lambda which does not capture anything, converts implicitly to function pointer:

auto const f = []( std::string& s ) { classA()( s ); };
testClass( f, "blah" );

Or use an ordinary named function.

Standard library algorithms can use either raw function pointer or class instance with operator() , because the type is templated.


The more general question, how to convert a possibly stateful functor object to raw function pointer, eg for C callback, is more interesting.

The standard library unfortunately lacks support for that.

Rewrite the testClass function as a function template.

template<typename T>
void testClass(T fun, std::string& name) {
  fun(name);
}

Then it can accept function pointers as well as instances of function objects.

That's simpler than triggering the std::function machinery.

There is this flexibility in C++ function pointers where you're not forced to write *fun() to call the pointed-to function. You can just write fun() .

And by the way, you can also pass in references-to-functions.

You can also use a thread-local stack of function objects. This would be something like

template<typename Sig> struct horrible_hackery;
template<typename Ret, typename... Args> struct horrible_hackery {
    static thread_local std::stack<std::function<Ret(Args...)>> functions;
    static Ret trampoline_callback(Args... args) {
        return functions.top()(args...);
    }
    template<typename F, typename Around> static void execute_around_fptr(F f, Around a) {
        functions.push(f);
        a(&trampoline_callback);
        functions.pop();
    }
};

Now you can use

ClassA obj;
horrible_hackery<void(std::string&)>::execute_around(obj, [&](void(*fptr)(std::string&)) {
    return testclass(fptr, str);
});

There is a C++03 version of this which is even more terrible terrible hackery that you don't want to think about.

If you're truly desperate for this feature and you can't accept the purely stack-based solution above, you can download a JIT compiler for the platform of your choice like LLVM and then JIT a thunk for yourself. This is pretty extreme though.

However, these limitations and generally horrible hackery leads one to the following conclusions:

  • Prefer a templated function object type if possible.
  • If not possible, use std::function which handles this in the optimal possible fashion.
  • Do not ever use function pointers or their even-more-evil siblings, member function pointers, unless you have some concrete requirement like dodging ABI bullets.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM