2012-05-11 2 views
1

비 가상 다형성로 전환 ++ 인터페이스변환 나는 현재 C를

void foo() ... 

및 implementions 많이

현재 foo는 다음과 같이 정의된다

void A::foo(); 
void B::foo(); 
struct Wrapper 
{ 
    void foo() 
    { 
     if(state == STATE_A) a.foo(); 
     else if(state == STATE_B) b.foo(); 
    } 
    union { 
     A a; 
     B b; 
    }; 
    State state; 
} 
//.... 
Wrapper a(STATE_A, existingA); 
Wrapper b(STATE_B, existingB); 
a.foo(); b.foo(); 

거기가 다음과 이것을하는 더 청결한 방법? 나는 여러개의 foo() 함수를 가지고 있고 여러개의 A/B 클래스를 가지고있다. 지루하고/오류가 발생하여 모든 경우를 기록합니다.

가상 함수를 사용할 수 없다는 것을 유의하십시오 (이것은 N^5 루프 내에서 실행됩니다 ... 1 천만 회 실행 + 초). 나는 컴파일러가 이것을 인라인하기를 원한다.

나는

+1

'모든 경우를 작성하는 것이 지루하고 오류가 발생하기 쉽습니다.'언어 기능을 삭제하고 직접 코드를 작성하도록 선택하면 어떻게됩니까? 이것이 바로 이러한 언어 기능이 존재하는 이유입니다. 누군가가 상용구를 작성하는 것에 지쳐 있기 때문입니다. –

+1

가상 디스패치를 ​​사용하는 것보다 어떻게 추가 할 수 있습니까? if-else 체인보다는 인덱스 (예 : 배열 조회)로 최적화 된 경우에도 대부분의 컴파일러 구현 측면에서 가상 디스패치를 ​​사용하는 것과 동일합니다. 즉, STATE_X는 vtable을 나타내며 Wrapper :: foo는 vtable 조회와 같습니다. –

+3

사실 가상 함수를 사용하여 직접 구현을 측정 했습니까? 3-5 "if"조건이 가상 함수 호출의 비용과 쉽게 일치 할 수 있습니다. 그리고 함수의 크기가 충분히 커지면 인라인이 실제로 발생한다는 보장은 없습니다 ... –

답변

2

내가 인라인 컴파일러를 원하는 A의, B의 등을 함께 수집의 생각과 데이터 중심의 방식으로 그들을 계산하지만, 불행히도 나는 (때문에 알고리즘 우려) 그렇게 할 수있다 이, 열심히.

그건 일어나지 않을 것입니다.

런타임 다형성을 사용 중입니다. 정의에 따라 컴파일러 은 호출 시간에 호출되는을 알 수 없습니다. 수동으로 수행하든 컴파일러에서 수행하든 상관없이 가상 디스패치에 대한 비용을 지불하게됩니다.

인라인은 회원 기능에 대한 호출에 있습니다. 그것은 여전히 ​​"인라인"부분에 도달하기 위해 메모리 액세스 ("유형"가져 오기)를 기반으로 조건부 분기를 수행해야합니다. 그리고 새로 추가 할 때마다 해당 분기에 조건이 추가됩니다. 기껏해야, 이것은 상태 테이블이 될 것입니다 ... 이것은 가상 함수 포인터와 다르지 않습니다 : 메모리 주소에서 가져와 특정 코드 조각으로 분기하는 데 사용합니다.

vtable 포인터처럼 컴파일러가 수행 할 수있는 작업을 구현하는 데 시간을 낭비 할뿐입니다.

프로필 나는 당신의 손으로 작성한 방법이 컴파일러를 능가 할 수 있다고 간단히 가정하는 대신 강력하게 권고합니다. 당신은 언어 수준의 다형성을 포기하기로 결정했습니다 경우


는, 당신은 대신 boost.variant 적절한 방문자를 사용해야합니다. 코드는 다음과 같습니다

typedef boost::variant<A, B> Wrapper; 

struct FooVisitor : public boost::static_visitor<> 
{ 
    template <typename T> void operator()(T &t) {t.foo()}; 
}; 

당신은 당신이 전화를 할 모든 기능에 대한 FooVisitor을해야 할 것이다.를 호출하려면 다음을 수행하십시오

Wrapper a = existingA; 
boost::apply_visitor(FooVisitor(), a); 

를 분명히, 당신은 간단한 함수에서 그것을 포장 할 수 있습니다

template<typename Visitor> 
void Call(Wrapper &a) {boost::apply_visitor(Visitor(), a);} 
:

void CallFoo(Wrapper &a) {boost::apply_visitor(FooVisitor(), a);} 

사실, 당신이 이들의 전체 템플릿 가족을 만들 수 있습니다

매개 변수 전달은 허용되지 않습니다 (매개 변수를 방문자 자체에 저장해야 함). 그러나 반환 값을 가질 수 있습니다 (boost::static_visitor<Typename_Here> visi 선언에 반환 유형을 입력해야 함). 토르).

또한 boost::variant 개체는 값 의미를 가지므로 복사본은 내부 개체를 복사합니다. 실제 유형을 얻으려면 boost::get() 구문을 사용할 수도 있지만 이 실제로이 아닌 경우에는 제안하지 않습니다. 그냥 방문자를 사용하십시오.

+0

그래, 확실히 이것을 프로파일 링해야합니다. – jameszhao00

+0

또한, 현재 Wrapper를 인접 메모리에 할당하고 있습니다. 나는 새로운 배치를 사용할 수 있고 polymorphic 객체를 가지고있는 max (sizeof (A), sizeof (B)) 크기를 가진 무언가를 가질 수있다. – jameszhao00

1

두 가지 선택 사항이 있습니다. 컴파일 할 때 함수 선택을 할 수도 있고, 런타임에 할 수도 있습니다. 실행 시간이라면 기존의 가상 메커니즘보다 더 잘 수행하지 못할 것입니다. 컴파일하는 경우 사용할 각 유형마다 다른 코드가 필요하지만 템플릿을 사용하여 프로세스를 자동화 할 수 있습니다.

template<typename T> 
struct Wrapper 
{ 
    void foo() 
    { 
     t.foo(); 
    } 
    T t; 
}; 

은 물론이 예는 고도의 추상화이고 나는 Wrapper 클래스를 사용하여 템플릿을 직접 입력 사이의 차이를 볼 수 없습니다. 더 나은 답변을 얻으려면 예제를 조금 더 살피셔야합니다.

관련 문제