2016-10-07 2 views
4

저는 간단하지만 유연한 이벤트 시스템을 프로그래밍하려고합니다. (대부분 운동과 마찬가지로, 정말 좋은 이벤트 핸들러가있는 기존 라이브러리가 있다는 것을 알고 있습니다.) 저는 약간의 걸림돌을 겪었습니다.전화하기 전에 std :: function의 유효성을 확인 하시겠습니까?

대리자 (아마도 std :: bind를 사용하여 lambda를 통해 가능)를 통해 std::function이 유효한 함수인지/멤버 함수의 개체가 호출되기 전에 여전히 해당 개체가 있는지 확인할 수 있습니까? 나는 단순히 std::function's bool 연산자를 사용해 보았지만 아무런 성공도 거두지 못했습니다.

이상적으로 A. 델리게이트 함수 내부가 아닌 다른 곳에서 검사를하고 싶습니다. 그리고 체크되고있는 std :: function이 델리게이트가 아닐 때 여전히 유효합니다.

아이디어가 있으십니까? 편집

: 여기 내가 대신 벌거 벗은 사람의 당신은 스마트 포인터 (std::shared_ptr/std::weak_ptr)를 사용할 수 있습니다

#include <iostream> 
#include <string> 
#include <functional> 

class Obj { 
public: 
    std::string foo; 
    Obj(std::string foo) : foo(foo) {} 
    std::function<void()> getDelegate() { 
     auto callback = [this]() {this->delegatedFn();}; 
     return callback; 
    } 
    void delegatedFn() { 
     std::cout << foo << std::endl; 
    } 
}; 

int main() { 
    Obj* obj = new Obj("bar"); 
    std::function<void()> callback = obj->getDelegate(); 
    callback(); 
    delete obj; 

    //perform some type of check here whether function is valid, without needing to know whether the function is a delegate or not 
    if(callback) { 
     std::cout << "Callback is valid" << std::endl; //callback is still considered valid after obj is deleted 
     callback(); //no exception thrown, prints a random series of characters 
    } 
    else { 
     std::cout << "Callback is invalid" << std::endl; 
    } 

    return 0; 
} 
+0

시도해 보셨습니까? 바람직하게는 [Minimal, Complete, and Verifiable Example] (http://stackoverflow.com/help/mcve). 또한 오류를 포함하십시오. –

+5

임의의 포인터가 여전히 유효한 개체를 가리키는 지 확인할 수 없으며 사용 사례가 동일한 것으로 줄어 듭니다. –

+0

당신의 예제 코드를 보았을 때, 이것은'Obj' 클래스가'foo' 멤버 변수에 대한 포인터를 리턴 한 함수를 가지고 있고' obj'가 삭제되었습니다. 가능한 유일한 방법은 직접 추적하는 것입니다. 라이브러리 또는 컴파일러가 여기에서 도움을 줄 수있는 "마법"은 없습니다. –

답변

4

를 실행 한 테스트에 대한 소스입니다 :

#include <iostream> 
#include <string> 
#include <functional> 
#include <memory> 

class Obj { 
public: 
    std::string foo; 
    Obj(std::string foo) : foo(foo) {} 
    void delegatedFn() { 
     std::cout << foo << std::endl; 
    } 
}; 

int main() { 
    auto obj = std::make_shared<Obj>("bar"); 
    std::weak_ptr<Obj> ptr = obj; 
    std::function<void()> callback = [ptr](){ 
     auto sh = ptr.lock(); 
     if(sh) { std::cout << "valid" << std::endl; sh->delegatedFn(); } 
     else { std::cout << "invalid" << std::endl; } 
    }; 
    callback(); 
    obj = nullptr; 
    callback(); 
    return 0; 
} 

이 경우 (유효 기간 :)의(유효 기간 :)의 뭔가은 매달려있는 포인터를 캡처합니다.
대신 참조 된 개체가 함수 자체 내에서 아직 살아 있는지 확인합니다.

2

내가 사용하는 방송/리스너 패턴은 ​​다음과 같습니다

template<class...Args> 
struct broadcaster { 
    std::vector< std::weak_ptr< std::function<void(Args...)> > > callbacks; 

    void operator()(Args...args) const { 
    std::remove_erase_if(begin(callbacks), end(callbacks), [](auto&& ptr){return !ptr;}); 
    auto tmp = callbacks; 
    for (auto pf : tmp) { 
     if (pf && *pf) (*pf)(args...); 
    } 
    } 
    std::shared_ptr<void> listen(std::shared_ptr<std::function<void(Args...)>> f) { 
    callbacks.push_back(f); 
    return f; 
    } 

    std::shared_ptr<void> listen(std::function<void(Args...)> f) { 
    auto ptr = std::make_shared<std::function<void(Args...)>>(std::move(f)); 
    return listen(ptr); 
    } 
}; 

리스너 메시지 .listenbroadcaster과의 콜백. 그들은 shared_ptr<void> 토큰을 돌려받습니다.

는 너무 오래 그 토큰이 존재하는 한, 방송사는. 전달 된 함수 객체에 메시지를 보내드립니다

Obj

std::vector<std::shared_ptr<void>> tokens 또는 단일 std::shared_ptr<void>를 저장하는 것 중 하나. 파괴되었을 때 청취자는 자동으로 등록 취소됩니다.

또는 Objshared_from_this을 상속 할 수 있습니다. 그런 다음 Obj 인스턴스 자체의 수명 (shared_ptr의 에일리어싱 생성자를 사용) 공유

std::function<void()> delegate; 
std::shared_ptr<std::function<void()>> getDelegatedFn() { 
    if (!delegate) delegate = [this]{ this->delegateFn(); } 
    return { 
    &delegate, 
    shared_from_this() 
    }; 
} 

을 구현한다. 이것을 listen에 전달하고 완료하십시오.

관련 문제