Динамически создавать указатель на обобщенную функцию из указателя метода, получая возвращаемое значение и параметры - PullRequest
2 голосов
/ 29 декабря 2011

У меня есть этот вспомогательный класс, который я использую для вызова методов-членов для кода, ожидающего статические функции Си. Эта конкретная «версия» совместима с обратными вызовами Windows LPTHREADROUTINE, принимая в качестве параметра функцию DWORD (class::method) (void *), называемую следующим образом:

CreateThread(NULL, 0, runThreadFunction<SomeClass>, makeThreadInfo(&myClass, &SomeClass::ThreadFunction, NULL), 0, NULL);

Я хочу сделать все это обобщенным, и я знаю, что это можно сделать с помощью нового стандарта C ++ 11, но я не могу его осуществить.

#pragma once
#include <stdafx.h>

template <typename C>
struct ThreadInfo
{
    // we have an object
    C* obj;
    // and that object has a function that takes void* and returns DWORD, and so is suitable for a threadproc (except for it being a member function)

    DWORD (C::* function)(void*);
    // and we have any amount of extra data that we might need.

    void* data;
    // default copy c-tor, d-tor and operator= are fine

    ThreadInfo(C* o, DWORD (C::*func)(void*), void* d) : obj(o), function(func), data(d)
    {
    }
};

template <typename C>
DWORD WINAPI RunThreadFunction(void* data)
{
    shared_ptr<ThreadInfo<C> > ti((ThreadInfo<C>*)data);
    //ThreadInfo<C>* ti = (ThreadInfo<C>*) data;
    return ((ti->obj)->*(ti->function))(ti->data);
}

template <typename C>
void* MakeThreadInfo(C* o, DWORD (C::* f)(void*), void* d)
{
    return (void*)new ThreadInfo<C>(o, f, d);
}

Я попытался изменить интерфейс функции MakeThreadInfo на что-то вроде этого:

template <typename C, typename R, typename... P>
void* MakeThreadInfo(C* o, std::function<R(P&...)> f, void* d)

Казалось бы, это путь, но я не смог затем передать это значение вверх по течению.


Вот что я хочу получить:

Имеется класс MyClass с методом MyMethod и обратный вызов переменная тип возвращаемого значения и один или несколько параметров различных типов (последний из которых void *userData) Как я могу, с наименьшим количеством шаблонов, передать что-то обратному вызову и заставить его в свою очередь вызвать MyClass :: MyMethod.

Для иллюстрации:

typedef bool (*Callback1)(void *userData);
typedef int  (*Callback2)(bool param, void *userData);

void TheirLibrary::Function1(Callback1 callback, void *userData);
void TheirLibrary::Function2(Callback2 callback, void *userData);

class MyClass
{
    bool MyMethod1(void *userData);
    int  MyMethod2(bool someParam, void *userData);

    void DoSomething()
    {
        Function1(CreateGenericCPointer(&MyClass::MyMethod1), &MyClass);
        Function2(CreateGenericCPointer(&MyClass::MyMethod2), &MyClass);
    }
}

Что такое допустимая реализация CreateGenericCPointer?

Ответы [ 3 ]

1 голос
/ 29 декабря 2011

Посмотрите, хотите ли вы этого. Вам все еще нужно указать тип возвращаемого значения, но он (на мой взгляд) хорошо определен как параметр шаблона для struct, который содержит static функции-оболочки. Вы все еще можете улучшить его, если вам нужна более высокая степень гибкости для TTst - я не уверен, как вы хотите определить функции-члены для вызова, поэтому я сохранил их подпись как callback.

#include <iostream>

typedef int (*TFoo00)( void * );
typedef bool (*TFoo01)( int, void * );

void Bar00( TFoo00 fnc, void * ud )
{
 std::cout << "Bar00" << std::endl;
 fnc( ud );
}
void Bar01( TFoo01 fnc, void * ud )
{
 std::cout << "Bar01 " << std::endl;

 fnc( -1, ud );
}

class TTst;

template< typename PResult >
struct TWrap
{

  static PResult Call( void * ud )
  {
   std::cout << "TWrap::Call( P00 )" << std::endl;
   return ( static_cast< TTst * > ( ud )->Foo00() );
  }
  template< typename P00 >
  static PResult Call( P00 u00, void * ud )
  {
   std::cout << "TTst::Call( P00, P01 )" << std::endl;
   return ( static_cast< TTst * > ( ud )->Foo01( u00 ) );
  }
};

class TTst
{
 public:
  int Foo00( void )
  {
   std::cout << "TTst::Foo00" << std::endl;
   return ( 0 );
  }
  bool Foo01( int u00 )
  {
   std::cout << "TTst::Foo01 : "  << u00 << std::endl;
   return ( u00 != 0 );
  }

  void Do( void )
  {
   Bar00( TWrap< int >::Call, this );
   Bar01( TWrap< bool >::Call, this );
  }

};

int main( void )
{
 TTst lT;

 lT.Do();

 return ( 0 );
}

РЕДАКТИРОВАТЬ: измененные аргументы в Bar01 - я не заметил, что он принимает 2 аргумента как Bar00 ... Просто чтобы уточнить, вам нужно определить одну шаблонную Call функцию для всех Callback, которые иметь одинаковое количество аргументов.

1 голос
/ 29 декабря 2011

Мне не совсем ясно, какой уровень универсальности вы ищете, но, возможно, это поможет вам начать:

typedef std::function<DWORD()> ThreadFuncT;

DWORD WINAPI RunThreadFunction(void* data)
{
    std::unique_ptr<ThreadFuncT> tf(static_cast<ThreadFuncT*>(data));
    return (*tf)();
}

template<typename F>
ThreadFuncT* MakeThreadFunction(F&& f)
{
    return new ThreadFuncT(std::forward<F>(f));
}

// ...

auto myClass = std::make_shared<SomeClass>(/* ... */);
CreateThread(
    nullptr,
    0,
    RunThreadFunction,
    MakeThreadFunction([=]() { return myClass->ThreadFunction(nullptr); }),
    0,
    nullptr
);

Обратите внимание, что, поскольку myClass является std::shared_ptr<> и захваченопо значению, базовое время жизни SomeClass завершится должным образом, даже если myClass выйдет из области действия до завершения выполнения потока (, пока в конечном итоге вызывается RunThreadFunction).


РЕДАКТИРОВАТЬ: Вот еще один подход (не проверено, могут быть синтаксические ошибки):

template<typename R>
R WINAPI RunThreadFunction(void* data)
{
    typedef std::function<R()> ThreadFuncT;
    std::unique_ptr<ThreadFuncT> tf(static_cast<ThreadFuncT*>(data));
    return (*tf)();
}

template<typename F>
auto MakeThreadFunction(F&& f) -> std::function<decltype(f())()>*
{
    return new std::function<decltype(f())()>(std::forward<F>(f));
}

// ...

auto myClass = std::make_shared<SomeClass>(/* ... */);
auto f = [=]() { return myClass->ThreadFunction(nullptr); };
CreateThread(
    nullptr,
    0,
    RunThreadFunction<decltype(f())>,
    MakeThreadFunction(std::move(f)),
    0,
    nullptr
);
0 голосов
/ 29 декабря 2011

РЕДАКТИРОВАТЬ: Это не совсем то, что нужно ОП. ОП нуждается в общей версии этого.

Почему бы не использовать функцию std :: полностью?

Это будет выглядеть как

std::function<void(void)> myFunc = std::bind(&Class::memberFunc,&classInstance);
CreateThread(NULL,0,runStdFunction, new std::function<void(void)>(myFunc),0,NULL);

runStdFunction будет таким же простым, как круговая

Я использую typedef std::function<void(void)> voidFunction для простоты.

void runStdFunction(void *data)
{
   std::unqiue_ptr<voidFunction> func (static_cast<voidFunction*>(data));
   (*func)();
}

Идея проста, все, что вы делаете, это проходите через std :: functions и затем вызываете их. Оставьте все преобразования в / из функций-членов / etc в std :: bind.

Конечно, ваш тип возврата не является недействительным, но это незначительная деталь.

...