2017-01-17 12 views
0

Мне нужно было что-то вроде std :: function, но затем я нашел this, который быстрее (как утверждает автор) и может даже сравниваться с оператором ==. Я приспособил его, чтобы динамический тип возвращаемого значения и аргументы, как это:Пользовательская реализация std :: function-like как для бесплатных, так и для функций-членов

template<typename TReturn, typename... TArgs> 
class Delegate {}; 

template<typename TReturn, typename... TArgs> 
class Delegate<TReturn(TArgs...)> final 
{ 
private: 
    typedef void* InstancePointer; 
    typedef TReturn (*InternalFunction)(InstancePointer, TArgs...); 

private: 
    // Turns a free function into our internal function stub 
    template <TReturn (*FreeFunction)(TArgs...)> 
    static TReturn FreeFunctionStub(InstancePointer instance, TArgs... args) { 
     // We don't need the instance pointer because we're dealing with free functions 
     return (FreeFunction)(std::forward<TArgs>(args)...); 
    } 

    // Turns a member function into our internal function stub 
    template <class TClass, TReturn (TClass::*MemberFunction)(TArgs...)> 
    static TReturn MemberFunctionStub(InstancePointer instance, TArgs... args) { 
     // Cast the instance pointer back into the original class instance 
     return (static_cast<TClass*>(instance)->*MemberFunction)(std::forward<TArgs>(args)...); 
    } 

public: 
    Delegate() = default; 

    // Resets this delegate to a new free function 
    template <TReturn(*FreeFunction)(TArgs...)> 
    void reset() { 
     m_instance = nullptr; 
     m_function = &FreeFunctionStub<FreeFunction>; 
    } 

    // Resets this delegate to a new member function 
    template <class TClass, TReturn(TClass::*MemberFunction)(TArgs...)> 
    void reset(TClass* instance) { 
     m_instance = instance; 
     m_function = &MemberFunctionStub<TClass, MemberFunction>; 
    } 

    // Resets this delegate to a new free function 
    void specialReset(TReturn(*FreeFunction)(TArgs...)) { 
     m_instance = nullptr; 
     m_function = ??? 
    } 

    // Resets this delegate to a new member function 
    template<class TClass> 
    void specialReset(TClass *instance, TReturn(TClass::*MemberFunction)(TArgs...)) { 
     m_instance = instance; 
     m_function = ??? 
    } 

    // Invokes this delegate 
    TReturn invoke(TArgs... args) const { 
     if (m_function == nullptr) 
      throw new std::runtime_error(""Unbound delegate! Call reset() first.""); 

     return m_function(m_instance, std::forward<TArgs>(args)...); 
    } 

private: 
    InstancePointer m_instance; 
    InternalFunction m_function; 
}; 

Использование идет так:

Delegate<void()> del1; 
Delegate<int(double)> del2; 

del1.reset<&someParameterlessVoidFreeFunction>(); 
del1.invoke(); 

del2.reset<SomeClass, &SomeClass::someIntMemberFunction>(&someClassInstance); 
del2.invoke(24.2); 

То, что я пытаюсь сделать, это добиться чего-то вроде этого (ИМО, гораздо чище и интуитивное):

Delegate<void()> del1; 
Delegate<int(double)> del2; 

del1.reset(&someParameterlessVoidFreeFunction); 
del1.invoke(); 

del2.reset(&SomeClass::someIntMemberFunction, &someClassInstance); 
del2.invoke(24.2); 

Однако, я не совсем понимаю m_function концепцию. То, что я пытаюсь достичь, даже возможно? Как я мог это сделать?

Кроме того, что именно представляет собой часть класса Делегат и почему нам нужно определить class Delegate {};?

+0

Является ли это для академической причины или практического? Для практического, вы, вероятно, сохраните много головных болей, используя 'std :: function' в любом случае, даже если это, как утверждается, медленнее. Вам, похоже, не нужна функция 'operator =='. – AndyG

+0

Оператор == используется где-то, что не имеет значения. Я знаю о std :: function (это было сказано в самом начале), и я не хочу/не могу его использовать. –

+0

Хорошо, я упомянул, что ваш синтаксис 'del2.reset (& SomeClass :: someIntMemberFunction, & someClassInstance);' вряд ли будет работать без какого-либо действительно взломанного кода, который устанавливает статические данные. Это связано с тем, что разница между передачей указателя функции в качестве аргумента шаблона в качестве аргумента параметра заключается в том, что значение аргумента шаблона известно во время компиляции (указатель функции является целым типом), а аргумент параметра неизвестен до времени выполнения , – AndyG

ответ

0

Правильно, поэтому после нескольких дней исследований, разработки и тестирования я составил то, что делает быстрее std::function/std::bind с гораздо лучшим синтаксисом.

Для тех, кто интересуется, взгляните на Delegator. Я не могу вставить весь код здесь, потому что он слишком большой, чтобы его можно было публиковать здесь. Кроме того, по этой ссылке вы всегда будете иметь последнюю версию.

С доверителем, вы можете сделать следующее:

Delegate<void(void)> d1; // Blank delegate 
Delegate<void(void)> d2(&freeFunction); // Delegate to a free function 
Delegate<void(void)> d3(&SomeClass::someClassStaticFunction); // Delegate to a static function 
Delegate<void(void)> d4(&SomeClass::someClassFunction, &someClassInstance); // Delegate to a member function 
Delegate<void(void)> d5(&SomeClass::someClassConstFunction, &someClassInstance); // Delegate to a member const function 

d1.reset(); // Resets the delegate 
d1.reset(&freeFunction); // Resets the delegate to a free function 
d1.reset(&SomeClass::someClassStaticFunction); // Resets the delegate to a static function 
d1.reset(&SomeClass::someClassFunction, &someClassInstance); // Resets the delegate to a member function 
d1.reset(&SomeClass::someClassConstFunction, &someClassInstance); // Resets the delegate to a member const function 
d1.reset(&d2); // Resets the delegate to d2's state 
0

В основном это решение для вашего синтаксиса, чтобы стать ближе к тому, что мы будем делать, чтобы реализовать std::function и скорость жертвы гибкости (за счет использования времени выполнения полиморфизм вместо чистого шаблонам)

Первый вызов может быть решена путем добавления еще одной переменной-члена к вашему Delegate классу провести свободные функции:

template<typename TReturn, typename... TArgs> 
class Delegate<TReturn(TArgs...)> final 
{ 
    // ... 
    typedef TReturn(*FreeFunctionType)(TArgs...); 
    FreeFunctionType m_free_function = nullptr; 
}; 

Теперь вы можете установить m_free_function в specialReset, в то время как возвратные другие члены:

void specialReset(TReturn(*FreeFunction)(TArgs...)) { 
    m_instance = nullptr; 
    m_function = nullptr; 
    m_free_function = FreeFunction; 
} 

И называть его выполняет дополнительную проверку нулевой:

TReturn invoke(TArgs... args) const { 
    if (m_function == nullptr && m_free_function == nullptr) 
     throw new std::runtime_error("Unbound delegate! Call reset() first."); 
    else if (m_function) 
     return m_function(m_instance, std::forward<TArgs>(args)...); 
    else return m_free_function(std::forward<TArgs>(args)...); 
} 

и тест:

void foo() 
{ 
    std::cout << "Called foo()\n"; 
} 

int main() 
{ 
    Delegate<void()> del1; 

    del1.specialReset(&::foo); 
    del1.invoke(); 
} 

Второй немного сложнее.

Идея для решения этого объявить указатель класса абстрактный базовый, что мы можем получить от позже сделать наш звонок:

template<typename TReturn, typename... TArgs> 
class Delegate<TReturn(TArgs...)> final 
{ 
private: 
    //... 
    struct ICall 
    { 
     virtual TReturn doCall(TArgs... args) = 0; 
    }; 
    std::unique_ptr<ICall> caller; 
}; 

Обычно этот указатель будет nullptr, который мы можем проверить на, но в противном случае мы можем вызвать функцию в invoke:

// Invokes this delegate 
TReturn invoke(TArgs... args) const { 
    if (m_function == nullptr && m_free_function == nullptr && caller == nullptr) 
     throw new std::runtime_error("Unbound delegate! Call reset() first."); 
    else if (m_function) 
     return m_function(m_instance, std::forward<TArgs>(args)...); 
    else if (caller) 
     return caller->doCall(std::forward<TArgs>(args)...); 
    else 
     return m_free_function(std::forward<TArgs>(args)...); 
} 

и, наконец, осуществление нашей второй specialReset где мы создаем производный класс и установить наш указатель:

template<class TClass> 
void specialReset(TClass *instance, TReturn(TClass::*MemberFunc)(TArgs...)) { 
    m_instance = nullptr; 
    m_function = nullptr; 
    m_free_function = nullptr; 
    struct DerivedCall : public ICall 
    { 
     DerivedCall(TClass* _instance, TReturn(TClass::*_func)(TArgs...)) : m_instance(_instance), m_func(_func){} 
     TReturn doCall(TArgs... args) override 
     { 
      return (m_instance->*m_func)(std::forward<TArgs>(args)...); 
     } 
     TClass* m_instance; 
     TReturn(TClass::*m_func)(TArgs...); 
    }; 
    caller = std::make_unique<DerivedCall>(instance, MemberFunc); 
} 

И тест:

struct A{ 
    int foo(double){std::cout << "Called A::foo\n"; return 42;} 
}; 

int main() 
{ 
    Delegate<int(double)> del2; 
    A a; 

    del2.specialReset(&a, &A::foo); 
    del2.invoke(24.2); 
} 

Demo

Я уверен, что кто-то более умный, чем я могу придумать что-то лучше. Как я сказал.Вероятно, мы уже потеряли драгоценную скорость, за которую вы выступали за более чем std::function из-за runtimeyness этого всего, и я не уверен, что он делает для operator== (Вероятно, это стало недопустимым, когда я создал m_free_function). Мы не можем использовать specialReset с lambdas здесь (... еще).

+0

Спасибо за ваше время! После поиска часов я наконец нашел это: http://pastebin.com/tkuAgzSc К сожалению, даже подумал, что использование ТОЧНО, что я хотел, это не помогает мне, потому что вам нужно указать тип указателя функции (либо (*) для бесплатных функций или (SomeClass :: *) для функций-членов) в качестве аргумента шаблона. Это плохо, потому что класс будет хранить этих делегатов как часть их членов, и он не знает, какой именно делегат он будет (это система событий, над которой я работаю). Кроме того, я читал о C++ 11 'decltype', но все же, никакого успеха. Есть идеи? –

+0

@YvesHenri: Вы пренебрегали упоминанием, как мой ответ вам не поможет. Причина, по которой все эти подходы принимают указатель на функцию в качестве аргумента шаблона, заключается в том, что они известны во время компиляции и могут быть хорошо обернуты. Если вам нужен ваш синтаксис, вам придется успеть на стирание стирания во время выполнения, что происходит медленнее. – AndyG

+0

Это не помогает мне, потому что, как вы уже сказали, мы теряем скорость и оператор ==, не говоря уже о запутанном коде для 'specialReset (member_function)'. Ссылка, которую я опубликовал в своем ответе BEAUTIFULY, делает то, что я просил (это даже быстро и сопоставимо), но тогда мы теряем гибкость в объявлении объекта Delegate, который должен работать как с бесплатными, так и с функциями-членами (несколько вызовов сброса для разных функций) , Я хочу немного посредине двух. –

 Смежные вопросы

  • Нет связанных вопросов^_^