2010-11-20 7 views
1

라이브러리 API에서 함수 포인터를 설정해야 특정 작업을 수행해야 할 때마다 해당 함수를 호출 할 수 있습니다. 내 주요 소스에서사용에 관한 딜레마 멤버 함수에 대한 포인터

int (*send_processor)(char*,int); 
int setSendFunctor(int (*process_func)(char*,int)) 
    { 
     send_processor = process_func; 
    } 

, 나는

int thisclass::thisfunction(char* buf,int a){//code} 

thisfunction는 thisclass의 다른 기능에 따라, 클래스의 멤버 함수로 정의이 기능을 가지고있다. 그래서 나는 그것을 수업에서 분리 할 수 ​​없다.

setSendFunctor의 멤버 함수에 대한 포인터를 설정할 수 없습니다. API 내부에서 멤버 함수의 포인터에 할당하지 않고 일반 함수 포인터 인 send_processor에 할당하기 때문입니다.

이 클래스는 파생 클래스가 아니기 때문에 가상 함수를 사용할 수 없습니다.

이 문제를 해결하는 가장 좋은 방법은 무엇입니까?

+0

답장을 보내 주셔서 감사합니다. Boost 나 다른 제 3 자 라이브러리를 사용할 수 없습니다. – SkypeMeSM

+0

사용중인 라이브러리를 바꿀 수 있습니까? – Stewart

+0

부스트 라이브러리는 어쨌든이 특별한 경우에만 제한된 도움을줍니다. 귀하의 질문을 잘못 언급 한 답변입니다. 어쨌든 어떤 식 으로든 도움이 될 수있는 부분은 C++ TR1의 일부이며, 컴파일러가이를 지원할 수도 있습니다. 그것은 표준의 일부입니다. – Omnifarious

답변

3

이 문제를 해결하는 일반적인 방법은 명시 적으로 this 포인터를 매개 변수로 사용하여 멤버 함수를 호출하는 정적 함수를 내 클래스에 만드는 것입니다. 때로는 정적 함수 대신 C 라이브러리가 콜백에 필요한 타입 시그니처를 가깝게 따라야 할 경우가 있는데,이 경우에는 void *이 그런 경우에 공통적으로 내가 원하는 실제로 this 포인터로 캐스팅 한 다음 멤버 함수를 호출합니다.

API에 콜백에 다시 제공 될 void *을 제공하는 기능이 없다면 해당 API가 손상되어 수정해야합니다. 문제를 해결할 수 없으면 전역 변수 (또는 작은 사촌 static)와 관련된 옵션이 있습니다. 그러나 그들은 추합니다.

나는 이러한 종류의 변수를 생성하고 정적 함수를 바인딩하기 위해 다소 모자이크가있는 템플릿 시스템을 만들었습니다. 그런 다음 이러한 종류의 API를 가진 것들에 전달할 수 있습니다. 그러나 나는 그것을 사냥해야만 할 것이고 나는 평범한 구식 세계에 대한 어떤 이득도 제공하지 않는다고 확신한다.

과 같이 보일 것이다 템플릿을 사용하지 않고 그 생각을 기반으로하는 솔루션 :

class InstanceBinder1 { 
    public: 
    static initialize(thisClass *instance, int (thisClass::*processor)(char *buf, int a)) { 
     instance_ = instance; 
     processor_ = processor; 
    } 

    static int processor(char *instance, int a) { 
     instance_->*processor_(buf, a); 
    } 

    private: 
    static thisClass *instance_; 
    static int (thisClass::*processor_)(char *buf, int a); 
}; 

그런 다음 당신은 C 라이브러리에 &InstanceBinder1::processor에 전달할 수 있습니다.

의 모든 개별 인스턴스에 대해이 유형의 새 클래스를 만들어야하는데 C 라이브러리를 호출해야합니다. 이는 컴파일 타임에 해당 인스턴스의 수가 결정된다는 것을 의미하며 그 주위에는 방법이 없습니다.

2

아쉽게도 C api가이 포인터를 처리하는 방법을 알지 못하기 때문에 멤버와의 포인터를 C API에 전달할 방법이 없습니다. 라이브러리를 변경할 수 없다면 막상 막히게됩니다.

API가 라이브러리를 통해 불투명 한 "컨텍스트"포인터를 전달하는 방법을 제공하는 경우 (Windows에서 CreateThread와 같이 수행되는 것처럼 매개 변수가 단순히 포인터 크기의 숫자로 처리되는 경우)) 그런 다음 정적 멤버 함수를 사용해야하며이 컨텍스트 매개 변수를 사용하여이 포인터를 전달해야합니다. 정적 멤버 함수에서 컨텍스트 매개 변수를 다시 포인터로 캐스팅하고이를 통해 멤버 함수를 호출합니다. 라이브러리가 변경 될 수 있거나 변경할 수있는 사람이 있고이 기능을 제공하지 않는 경우 오브젝트와 C 사이를 연결하는 가장 좋은 방법이기 때문에이 기능을 추가해야합니다.

불행히도 API는이를 수행하는 방법을 제공하지 않습니다. 귀하의 응용 프로그램이 단일 쓰레드이고, 라이브러리의 재진입이나 다중 사용자가 동시에 보장되지 않는다면,이 포인터를 전역 또는 클래스의 정적 멤버에 저장하는 것이 가능할 수도 있습니다. , 정적 멤버 함수를 다시 콜백으로 사용하고 매개 변수를 호출합니다.

세 번째 방법은 절대 포인터를 구성하고 올바른 멤버 함수로 점프하기 위해 실제로 실행 코드를 작성하는 작은 개체 인 "썽크"개체를 만드는 것일 수 있습니다. DEP와 사물을 가지고있는 현대의 시스템에서 이것은 번거롭게 할 가치가없는 어려운 접근입니다. ATL 라이브러리는 Windows에서이 작업을 수행하지만 어렵고 수년 동안 많은 보안 및 업데이트 문제를 야기했습니다.

+0

라이브러리를 변경할 수 없다고 가정합니다. 라이브러리를 변경할 수 있으면 그렇게해야합니다. – Stewart

+0

귀하의 의견은 당신이 대답을 편집 할 수 없다고 가정합니다. 대답을 편집 할 수 있다면 그렇게해야합니다. * 미소 짓다 * 물론 대답을 편집 할 수는 있지만 ... – Omnifarious

+0

@Omnifarious - 좋은 지적, 잘했다 :). 수정 된 답변. 나를 상기시켜 줘서 고마워. – Stewart

1

가장 간단한 방법은 전역 변수의 값을 설정하는 두 가지 기능 ("호출자"와 "설정자")을 만드는 것입니다. setter를 사용하여 프로그램의 해당 지점에서 전역 값을 설정하면 멤버 함수를 호출 할 객체가 생성됩니다. 호출자는 setSendFunctor으로 전달되고 호출되면 전역 변수의 값으로 호출이 전달됩니다.

저는 의도적으로 위의 전역 유형에 대해 언급하지 않았습니다. (일반적인 오래된 함수 포인터, 멤버 함수 포인터, 부스트 (Boost) 또는 다른 라이브러리의 펑터 또는 기타 편리 할 수있는 모든 것)가 될 수 있습니다.

저는 C API가 꽤 제한되어 있기 때문에 불행히도 전역이 없으면이를 수행 할 방법이 없다고 생각합니다.

+0

답장에서 지적했듯이, API가 재진입 적으로 호출되거나 다중 스레드로 동시에 호출 될 가능성이 전혀없는 경우에만 안전합니다. 이 두 가지가 사실이라면 항상 좋은 해결책입니다. 그렇지 않은 경우이 솔루션은 많은 문제를 일으킬 것입니다. – Stewart

+0

호출자와 설정자에서 선택한 스레드 동기화 메커니즘을 구현할 수있는 방법은 없습니다. – Jon

0

정확하게 char *와 int가 무엇을 사용하는지 모르겠습니다. C API에 의해 생성 된 것이라면 "컨텍스트"정보를 전달할 방법이 없기 때문에 할 수있는 일이 없습니다.

당신이 전달한 것을 참조하는 중 하나가 다시 호출되면, 원래의 "this"또는 "this"와 다른 char * 또는 int를 포함하는 구조체로 다시 매핑 할 수 있습니다 .

1

다른 모든 것이 실패하고 라이브러리에 클라이언트 정의 데이터가없는 함수 포인터 만 사용할 수있는 경우가 있고 여러 객체에서 콜백을 가져오고 콜백의 라이프 사이클이 겹치는 경우 트램펄린을 사용할 수 있습니다.

평소에이 방법을 사용했는데 (실제로 실제로는 2 ~ 3 번 정도만 했으므로 대부분의 라이브러리는 정상적으로 수행되기에 일반적으로 충분하지 않습니다.

int send_processor_trampoline (char* buf, int a) 
{ 
    return ((thisclass*) 0x12345678) -> thisfunction (buf, a); 
} 

당신은 다음 Windows에서 (이 기능에 대한 기계 코드를 검사 할 수 그것으로 스테핑과 얻어서 : 당신이 콜백을 설정 무효 * 사용자 데이터) 거기에 하드 코딩 된 포인터와 함수를 컴파일하는 것입니다 함수 포인터의 값, 그 위치에서 메모리를 검사). 일반적으로 하드 코드 된 포인터의 위치가 명확한 두 개의 12 바이트가 있습니다. 포인터 값을 변경하고 그렇지 않은 경우 diff합니다. 따라서 주어진 객체에 대한 멤버 함수의 호출은 동일한 기계 코드를 갖지만 하드 코딩 된 포인터의 바이트는 수신기 객체에 대한 포인터의 바이트로 대체됩니다.

thisclass 개체에 대한 포인터가 주어진 런타임시 trampolines를 만들려면 OS에서 쓸 수있는 실행 가능 메모리를 가져옵니다. 일반적으로이 코드는 청크로 표시되므로 trampolines의 관리자 개체를 만들어서 4K를 사용하지 마십시오. 각 20 바이트 함수), 그 바이트를 복사하여 하드 코딩 된 포인터의 바이트를 오브젝트의 포인터로 바꿉니다. 이제 특정 객체에 대한 멤버 함수를 호출 할 수있는 무료 함수에 대한 포인터가 생겼습니다.

관련 문제