2015-11-03 1 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 {...} 
}; 

К сожалению, я не могу изменить параметр SIZE класса данных, чтобы быть параметром выполнения без шаблона. Кроме того, мне нужно, чтобы фактический экземпляр генератора был временем выполнения, потому что пользователь может выбрать алгоритм генератора. Если возможно, я бы предпочел решение для типов (то есть без 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

Если вы можете превратить в шаблон вашего Generator класса, вы можете использовать метод шаблона подход, как следующий:

#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>>{}; 
} 

Конечно, для простоты я определен как static метода generate, но вы можете легко переключиться на нестационарную версию решения с несколькими изменениями (например, это может зависеть от того, что ваши классы Generator не имеют статуса или нет, а статический метод не может соответствовать вашим требованиям, я не могу сказать это из того, что я вижу в вашем вопросе).

EDIT

Я только что видел, что ваши ищет завод с генератором выбранного во время выполнения.

Это следует немного другое решение, может быть, это лучше подходит вашему требованию:

#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). Шаблон фабрики singleton используется для динамического создания новых экземпляров данных во время выполнения в соответствии с выбранными алгоритмами, определенными в реализации.

Проект разделен на две части (использование пространства имен). Первая часть состоит из класса Base и шаблона PseudoRandomGenerator и Factory для всех генераторов. Вторая часть предназначена для реализации различных алгоритмов генерации данных. Каждый из классов реализации во второй части можно разделить на отдельные файлы (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.

Измените этот пример, чтобы использовать безупречную пересылку, если необходимо.

Смежные вопросы