2017-04-06 77 views
1

在我的一個項目中,我過度使用boost-variant。在某個點,我超過了boost-variant的模板參數(20)的最大數量。因此,我通過將幾個boost-variant類型鏈接在一起(如鏈接列表)來推導出以下解決方案。從`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

首先,你可以簡單地增加這個限制20個固定的BOOST_MPL_LIMIT_LIST_SIZE

關於您的代碼:即使編譯,BaseVisitor::operator()也會進行無限遞歸,因爲那時候*this被視爲BaseVisitor。
爲了避免這種情況,你可以使用CRTP具有良好的derived()代替:

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()); 
    } 
}; 

然後把這個相同的operator()到您的派生類的範圍(另有新的隱藏的)(以及需要result_type對於模板類)。

DEMO

沒有別名

如說,在評論中,別名在每個派生類中被寫入。爲了擺脫它,我們可以在基類中的同一水平收集兩個operator()秒,(這裏visit)不同的命名導出功能:

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); 
    } 
}; 

留給我們:

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

DEMO

+0

感謝您的快速回復和您的不錯的解決方案。不幸的是,我有一些限制,不能使用'BOOST_MP_LIMIT_LIST_SIZE'。你的解決方案絕對有效。 CRTP對我來說依然不是那麼明顯。 :-) 我不知道是否有可能刪除派生類中的混亂(模板別名代碼)? – Aleph0

+0

@FrankSimon除了演示的第31行和第44行(不是模板類),否。但通過修改我們的代碼一點點...看到版本。 –

+0

真的很棒。消化您的解決方案需要一些時間。非常感謝。我將在我的生產代碼中使用它。 – Aleph0