2017-04-06 14 views
1

내 프로젝트 중 하나에서 과도하게 boost-variant을 사용하고 있습니다. 어느 시점에서 나는 boost-variant에 대한 템플릿 매개 변수 (20)의 최대 수를 초과했습니다. 따라서 여러 개의 링크 된 목록을 함께 연결하여 다음과 같은 솔루션을 도출했습니다.코드 복사를 제거하기 위해 boost :: static-visitor에서 파생합니다

#include <boost/variant.hpp> 
#include <iostream> 

template<int T> struct A { 
    const int value = T; 
}; 

typedef boost::variant< 
    A<20>,A<21>,A<22>,A<23>,A<24>,A<25>,A<26>,A<27>,A<28>,A<29>,A<30>,A<31>,A<32>,A<33>,A<34>,A<35>,A<36>,A<37>,A<38>,A<39> 
> NextVar; 

typedef boost::variant< 
    A<1>,A<2>,A<3>,A<4>,A<5>,A<6>,A<7>,A<8>,A<9>,A<10>,A<11>,A<12>,A<13>,A<14>,A<15>,A<16>,A<17>,A<18>,A<19>,NextVar 
> TVar; 

struct PrintVisitor : public boost::static_visitor<std::string> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 

    template<int T> 
    result_type operator()(const A<T>& a) { 
     return std::to_string(a.value); 
    } 
}; 

struct IntVisitor : public boost::static_visitor<int> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 

    template<int T> 
    result_type operator()(const A<T>& a) { 
     return a.value; 
    } 
}; 

template<int I> 
struct AddVisitor : public boost::static_visitor<int> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 

    template<int T> 
    result_type operator()(const A<T>& a) { 
     return a.value+I; 
    } 
}; 

int main(int argc, char **args) { 
    TVar x = A<35>(); 
    PrintVisitor v1; 
    std::cout << x.apply_visitor(v1) << std::endl; 
    IntVisitor v2; 
    std::cout << x.apply_visitor(v2) << std::endl; 
    AddVisitor<10> v3; 
    std::cout << x.apply_visitor(v3) << std::endl; 
} 

이 해결 방법이 내 문제를 얼마나 효과적으로 해결했는지 정말 놀랐습니다. 아직도 소금 한 알갱이가있었습니다.

result_type operator()(const NextVar& n) { 
    return n.apply_visitor(*this); 
} 

불필요한 코드 중복의 일종 것 같다 : 모든 방문자를 위해 나는 선을 포함했다. 더 심해지면, 내가 boost-variant에서 60 가지 이상의 유형을 필요로하는 경우. 내 시도 내 방문자 모두에게 공통 기본 클래스를 정의했다 :

template<typename T> 
struct BaseVisitor : public boost::static_visitor<T> { 
    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(*this); 
    } 
}; 

나는 아래와 같은 BaseVisitor에서 파생하는 문제를 해결할 것이라고 생각 :

struct PrintVisitor : public BaseVisitor<std::string> { 
    template<int T> 
    result_type operator()(const A<T>& a) { 
     return std::to_string(a.value); 
    } 
}; 

을하지만 그 대신 컴파일러가 불평 :

template-argument for "const A<T> &" could not be derived from "T19" 

이러한 종류의 문제에 가장 가까운 해결 방법은 무엇입니까?

답변

2

우선이 제한을 늘리면 BOOST_MPL_LIMIT_LIST_SIZE으로 고정됩니다.

코드 정보 : 컴파일해도 BaseVisitor::operator()*this의 무한 재귀를 그 시점에 BaseVisitor로 간주합니다.
것을 방지하려면, 대신 좋은 derived()을 가지고 CRTP를 사용할 수 있습니다

template<class Derived, typename T> 
struct BaseVisitor : public boost::static_visitor<T> { 
    using typename boost::static_visitor<T>::result_type; 

    Derived & derived() { return static_cast<Derived &>(*this); } 

    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(derived()); 
    } 
}; 

그런 다음 필요에 result_type뿐만 아니라 (파생 클래스의 (그렇지 않으면 새에 의해 숨겨진) 범위로이 같은 operator()을 가져 템플릿 클래스 용). 별명없이

DEMO

으로는 코멘트에, 별명은 각 파생 클래스에서 작성해야했다. 그것을 제거하기 위해, 우리는 기본 클래스에서 같은 수준에있는 두 개의 operator()의를 수집 할 수 있으며, (여기 visit)를 다르게 도출 된 기능을 이름 :

struct PrintVisitor : public BaseVisitor<PrintVisitor, std::string> { 
    template<int I> 
    std::string visit(const A<I>& a) { 
     return std::to_string(a.value); 
    } 
}; 
:

template<class Derived, typename T> 
struct BaseVisitor : public boost::static_visitor<T> { 
    using typename boost::static_visitor<T>::result_type; 

    Derived & derived() { return static_cast<Derived &>(*this); } 

    result_type operator()(const NextVar& n) { 
     return n.apply_visitor(derived()); 
    } 
    template<int I> 
    result_type operator()(const A<I>& a) { 
     return derived().visit(a); 
    } 
}; 

은 우리를 떠나

DEMO

+0

빠른 답장과 멋진 솔루션에 감사드립니다. 불행히도 제한이있어서'BOOST_MP_LIMIT_LIST_SIZE'를 사용할 수 없습니다. 당신의 솔루션은 확실히 작동합니다. CRTP는 여전히 그렇게 분명하지 않습니다. :-) 파생 클래스에서 혼란 (템플릿 별칭 코드)을 제거 할 수 있는지 궁금합니다. – Aleph0

+0

@FrankSimon 데모 (템플릿 클래스 제외)의 31 번과 44 번 행을 제외하고는 아니요.그러나 우리 코드를 조금 수정하면 ... 판을 보게됩니다. –

+0

정말 대단합니다. 솔루션을 소화하려면 시간이 필요합니다. 많은 감사합니다. 프로덕션 코드에서 사용하겠습니다. – Aleph0