2015-11-03 80 views
2

我有一個類存儲數據的一個固定大小的量替代虛擬模板

template<size_t SIZE> class Data {...}; 

現在我有不同的算法用於生成數據(例如僞隨機發生器):

class PseudoRandom1 { 
    template<size_t SIZE> Data<SIZE> generate(); 
}; 

class PseudoRandom2 { 
    template<size_t SIZE> Data<SIZE> generate(); 
}; 

現在我想有一個動態的運行時決定,以決定調用哪些發生器。使用虛擬模板(這我知道,是不可能的),這將是以下幾點:

class Generator { 
    virtual template<size_t SIZE> Data<SIZE> generate() = 0; 
}; 

class PseudoRandomX : public Generator { 
    template<size_t SIZE> Data<SIZE> generate() override {...} 
}; 

不幸的是,我不能改變的數據類的大小參數是一個非模板運行參數。此外,我需要實際的生成器實例作爲運行時決策,因爲用戶可以選擇生成器算法。 如果可能的話,我更喜歡類型安全的解決方案(即沒有boost :: any)。

我知道虛擬模板是不可能的。有沒有解決這個問題的另一種方法?

+1

你能模板生成器? – Jarod42

+0

你是說在運行時確定SIZE? – zdan

+0

是否可行的做一個潛在的'SIZE硬編碼列表?如果是這樣,有幾個有趣的技巧可以在幕後工作。 –

回答

1

以下變化至少可以用於某些目的,與您迄今爲止所提供的信息一致。

class Generator { 
public: 
    template< size_t SIZE > 
    Data<SIZE> generate() { 
     Data<SIZE> x; 
     vgenerate(SIZE, x.pointer_to_internals()); 
     return x; 
    } 
protected: 
    virtual void vgenerate(size_t size, InternalDataPointer *p) = 0; 
}; 

class PseudoRandomX : public Generator { 
    void vgenerate(size_t size, InternalDataPointer *p) override {...} 
}; 

,將一組不同的用途工作的另一個解決方案是

template< size_t SIZE > 
class Generator { 
public: 
    virtual Data<SIZE> generate() = 0; 
}; 

template< size_t SIZE > 
class PseudoRandomX : public Generator<SIZE> { 
    Data<SIZE> generate() override {...} 
}; 
2

如果你可以變成一個模板您發電機類,你可以使用如下一個模板方法途徑:

#include<cstdlib> 

template<size_t SIZE> class Data { }; 

template<size_t SIZE> 
class Generator { 
public: 
    using Ret = Data<SIZE>; 
    static Data<SIZE> generate() { }; 
}; 

template<class Gen> 
class PseudoRandomX { 
    typename Gen::Ret generate() { return Gen::generate(); } 
}; 

int main() { 
    auto prx = new PseudoRandomX<Generator<16>>{}; 
} 

當然,爲了簡單起見,我定義爲staticgenerate方法,但您可以輕鬆地切換到解決方案的非靜態版本,並進行一些更改(例如,它可能取決於您的類是否爲無狀態並且靜態方法無法滿足您的要求,我不能說它來自我在你的問題中看到的)。

編輯

我剛看到你正在尋找一個在運行時選擇的發電機的工廠。

它遵循一個稍微不同的解決方案,也許它更貼近您的需求:

#include<cstdlib> 
#include<memory> 

template<size_t SIZE> class Data { }; 

template<size_t SIZE> 
class Generator { 
public: 
    const static size_t gsize = SIZE; 
    using GData = Data<SIZE>; 
    static Data<SIZE> generate() { }; 
}; 

template<size_t SIZE> 
class BasePseudoRandom { 
public: 
    virtual Data<SIZE> generate() = 0; 
}; 

template <size_t SIZE, class Gen> 
class ConcretePseudoRandom: public BasePseudoRandom<Gen::gsize> { 
public: 
    typename Gen::GData generate() { return Gen::generate(); } 
}; 

class Factory { 
public: 
    template<class Gen> 
    static std::shared_ptr<BasePseudoRandom<Gen::gsize>> create(Gen*) { 
     return std::make_shared<ConcretePseudoRandom<Gen::gsize, Gen>>(); 
    } 
}; 

int main() { 
    Generator<16> gen; 
std::shared_ptr<BasePseudoRandom<16>> pr = Factory::create(&gen); 
    pr->generate(); 
} 

這種方式,您只需在工廠內把你的發電機,並取回在該生成器內置僞隨機數發生器,後者尊一個明確的界面。

2

這是一個使用CRTP的例子,它在運行時動態註冊一組生成器(c-startup)。單實例工廠模式用於根據實現中定義的選定算法在運行時動態創建新數據實例。

設計分爲兩部分(命名空間util)。第一部分由所有發生器的僞隨機發生器基礎和模板類和工廠組成。第二部分是爲了實現各種數據生成算法。第二部分中的每個實現類可以拆分爲單獨的文件(在我們的例子中是3個)。

Working example 1

#include <iostream> 
#include <string> 
#include <map> 
#include <memory> 

namespace PseudoRandomGeneratorTypes { enum : int { Type1, Type2, Type3 }; } 

namespace util { 

    template<size_t SIZE> 
    struct __Data { 
     int a; 
    }; 

    using Data = __Data<10>; 

    class PseudoRandomGenerator { 
    protected: 
     PseudoRandomGenerator() {} 

    public: 
     auto getType() const { return _type; } 
     virtual Data generate() const = 0; 
    protected: 
     int _type; 
    }; 

    template<int PRGType, typename PRGImpl> 
    class PRGTmpl : public PseudoRandomGenerator { 

    public: 

     static PseudoRandomGenerator* CreatePtr() { 
      return new PRGImpl(); 
     } 

     static const int TYPE; 

    protected: 
     PRGTmpl() { _type = TYPE; } 

    }; 

    class PseudoRandomGeneratorFactory { 
    public: 
     typedef PseudoRandomGenerator* (*psg)(); 

     static auto get() 
     { 
      static PseudoRandomGeneratorFactory fact; 
      return &fact; 
     } 

     auto Register(int id, psg m) 
     { 
      _map[id] = m; 
      return id; 
     } 

     auto Create(int id) 
     { 
      return _map[id](); 
     } 

    private: 
     PseudoRandomGeneratorFactory() {} 
     ~PseudoRandomGeneratorFactory() {} 

     std::map<int, psg> _map; 

    }; 

    template <int arbitaryPRGType, typename arbitaryPRGImpl> 
    const int PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::TYPE = PseudoRandomGeneratorFactory::get()->Register(arbitaryPRGType, &PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::CreatePtr); 

} 

namespace util { 

    class PRGType1 : public PRGTmpl < PseudoRandomGeneratorTypes::Type1, PRGType1 > { 
    public: 
     virtual Data generate() const override final { return Data{ 111 }; } 
    }; 
    template class PRGTmpl < PseudoRandomGeneratorTypes::Type1, PRGType1 >; 

    class PRGType2: public PRGTmpl < PseudoRandomGeneratorTypes::Type2, PRGType2 > { 
    public: 
     virtual Data generate() const override final { return Data{ 222 }; } 
    }; 
    template class PRGTmpl < PseudoRandomGeneratorTypes::Type2, PRGType2 >; 

    class PRGType3 : public PRGTmpl < PseudoRandomGeneratorTypes::Type3, PRGType3 > { 
    public: 
     virtual Data generate() const override final { return Data{ 333 }; } 
    }; 
    template class PRGTmpl < PseudoRandomGeneratorTypes::Type3, PRGType3 >; 

} 

using namespace util; 
using namespace std; 

int main() 
{ 

    auto rng1 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type1)); 
    auto rng2 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type2)); 
    auto rng3 = unique_ptr<PseudoRandomGenerator>(PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type3)); 

    cout << rng1->generate().a << endl; 
    cout << rng2->generate().a << endl; 
    cout << rng3->generate().a << endl; 
} 

此外,如果你一次只需要一個實例,並希望不使用堆的CreatePtr()FUNC。可以用以下代替。

static PseudoRandomGenerator* GetPtr() { 
    static PRGImpl _m; 
    _m = PRGImpl(); 
    return &_m; 
} 

專業化變爲:

template <int arbitaryPRGType, typename arbitaryPRGImpl> 
const int PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::TYPE = PseudoRandomGeneratorFactory::get()->Register(arbitaryPRGType, &PRGTmpl<arbitaryPRGType, arbitaryPRGImpl>::GetPtr); 

和使用模式改變爲:

auto rng1 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type1); 
auto rng2 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type2); 
auto rng3 = PseudoRandomGeneratorFactory::get()->Create(PseudoRandomGeneratorTypes::Type3); 

在第二個例子,我們可以使用一個普通的C風格陣列而不是地圖,以避免動態內存分配。

Working example 2

改編自 Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates

2

類型擦除是你的朋友。就你而言,你可以使用std::function,它提供了一半的樣板。另一半是編寫一個包裝,將generate轉換爲operator()

template<std::size_t N, class T> 
struct generator_adapter 
{ 
    auto operator()() { return generator_.template generate<N>(); } 
    T generator_; 
}; 

template<size_t Size> 
using AnyRandomGenerator = std::function< Data<Size>() >; 

template<size_t Size, class T> 
auto as_any_generator(T g) 
{ 
    return AnyRandomGenerator<Size>(generator_adapter<Size,T>{g}); 
} 

AnyRandomGenerator<4> f = as_any_generator<4>(PseudoRandom1()); 

generate功能將需要是公共的,或者你可以讓你的發電機generator_adapter的朋友。

修改此示例以在必要時使用完美轉發。