简体   繁体   中英

Convert from modern C++11 function to raw function pointer

Let's suppose I have the following function interface:

void giveme(void (*p)());

That function simply accepts a pointer to a function with no return type and argument.

I'm wondering if exists a way ( without change the interface ) to pass a class method as parameter of that function.

I'll try to explain better with an example. I have a class, like:

class Foo {
 public:
  template<typename T>
  void bar();
};

I want to pass bar<T> (of an addressable instance of the class) as parameter of the function giveme .

I thought to bind the method with an object, and obtain the function target.

Something like:

int main(int argc, char *argv[]) {
  Foo foo;
  std::function<void()> f = std::bind(&Foo::bar<int>, &foo);

  giveme(f.target<void()>());

  return 0;
}

It compiles, but obviously does not work because, from here :

TargetType shall match the target type, so that typeid(TargetType)==target_type() . Otherwise, the function always returns a null pointer.

So, if exists, what is a way to achieve it?

Here's one (very bad) idea:

Foo * foo_ptr;  // maybe thread_local

void foo_call()
{
    foo_ptr->bar<int>();
}

int main()
{
    Foo foo;
    foo_ptr = &foo;
    give_me(&foo_call);
}

It's not pretty, but neither is your situation.

There's only one way I know of, and it's a terrible idea, and don't do this.

typedef void (*void_fn)();
struct stateful_void_fn_data = {
    void_fn raw;
    std::function<void()> actual;
    std::atomic_bool in_use;
}
// a global array to hold your function bindings and such
extern stateful_void_fn_data stateful_functions[5];
// N stateless functions that defer to the correct global state
template<int n> void void_fn_impl() {stateful_functions[n].actual();}
extern stateful_void_fn_data stateful_functions[5] = 
    {{void_fn_impl<0>}, {void_fn_impl<1>}, {void_fn_impl<2>}, {void_fn_impl<3>}, {void_fn_impl<4>}};
// function to register a stateful and get a stateless back
void_fn allocate_void_fn(std::function<void()>&& f) {
   for(int i=0; i<5; i++) {
       if(stateful_functions[i].in_use.compare_exchange_weak(false, true)) {
            stateful_functions[i].actual = std::move(f);
            return stateful_functions[i].raw;
        }
   }
   throw std::runtime_error("ran out of stateful functions :(");
}
// function to unregister
void free_void_fn(void_fn f) {
    if (f == nullptr) return;
    for(int i=0; i<5; i++) {
        if (stateful_functions[i].raw == f) {
             stateful_functions[i].in_use = false;
             return;
        }
     }
   throw std::runtime_error("unknown void function");
}

Basically, I generate 5 void() functions ( void_fn_impl<N> ), and each calls a function stored in one of the five a global array slots ( stateful_functions[i].actual ). Then, allocate_void_fn will store any std::function<void()> in the global array, and hand you the void() that calls that entry in the array. This function itself is stateless, because we've stored all the state in the global array. free_void_fn and in_use exist solely to make the functions reusable.

And of course, because RAII is good:

class hidden_state_void_fn {
    void_fn raw;
public:
    hidden_state_void_fn(std::function<void()>&& f) 
         :raw(allocate_void_fn(std::move(f)) {}
    hidden_state_void_fn(const hidden_state_void_fn&& r) {
         raw = r.raw;
         r.raw = nullptr;
    }
    hidden_state_void_fn& operator=(const hidden_state_void_fn&& r)  {
         free_void_fn(raw);
         raw = r.raw;
         r.raw = nullptr;
    }
    ~hidden_state_void_fn() {free_void_fn(raw);}
    operator void_fn() {return raw;}
    operator()() {raw();}
};
std::map<int,std::function<void()>> tasks;

template<int n>
struct task_wrapper{
  static void f(){ if (tasks.count(n)) tasks[n](); }
  task_wrapper(std::function<void()> fin){ tasks[n]=fin; }
  ~task_wrapper(){ tasks.erase(n); }
  static std::shared_ptr< void(*)() > make(std::function<void()> fin){
    auto self=std::make_shared<task_wrapper>(fin);
    return { &f, fin };
  }
};

A task_wrapper<N>::make(func) return a shared pointer to a stateless function pointer that will call the stateful func .

We can use the the usual techniques to create an array of K function pointers of signature shared_ptr<void(*)()>(*)() . Then we can have a shared_ptr<void(*)()> register_func( std::function<void()> ) .

To find blanks, we can either do a linear search, or we could build a table of blanks. This could look like a traditional allocation/free "heap", or a range-tree of blanks, or whatever.


Another approach would be to literally create and save a DLL on the fly then load it and call the symbol. This could be done via hacks (have such a DLL and a known offset to modify, copy and write, then load and run) or by shipping a C++ compiler (or other compiler) with your code (!).

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