2013-12-08 6 views
0

Упрощенная у меня есть следующие иерархии классов:Избегайте включения шаблона параметров

class BaseVec { 
    public: 
    BaseVec() {}; 
    virtual ~BaseVec() {}; 

    virtual double get_double(int i) const = 0; 
}; 

template<typename T> 
class Vec : public BaseVec { 
    public: 
    Vec() { ... }; 
    ~Vec() { ... }; 

    T get(int i) const { ... }; 

    double get_double(int i) const { 
     return get(i); 
    }; 
}; 

В моем проекте я неоднократно в конечном итоге с кодом следующего вида:

template<typename T> 
double foo_template(void* p) { 
    Vec<T>* v = reinterpret_cast<Vec<T>*>(p); 
    // use v which involves calling get 
    return result; 
} 

double foo(void* p, int type) { 
    if (type == 0) { 
    return foo_template<double>(p); 
    } else if (type == 1) { 
    return foo_template<int>(p); 
    } else if (...) { 
    // etc. 
    } else { 
    //unsupported type 
    } 
} 

(я мог бы использовать переключатель и использовать перечисления или первый лить p до BaseVec, а затем сделать dynamic_cast с, но логика остается неизменной)

Это не идея l поддерживать. Например, когда я добавляю дополнительный класс, который я хочу поддержать, я должен добавить предложение к каждому из блоков if-else-if.

Одним из возможных способов упрощения этого метода было бы лить p на BaseVec* и использовать метод get_double. Однако, поскольку этот метод называется очень часто, это приводит к низкой производительности. Кроме того, это не всегда возможно: иногда я хочу вызвать метод get, поскольку возвращаемый тип важен.

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

Есть ли способ сделать этот код более удобным для обслуживания?

PS: У меня нет (много) контроля над тем, что приходит в foo. foo вызывается внешней программой (точнее, R). Поэтому я могу передавать общие указатели, int, double и векторы символов до foo.

PPS: Предложения по улучшению названия также приветствуются.

ответ

0

Почему бы не изменить foo_templat е быть:

template<typename T> 
double foo_template(Vec<T>*) { 
    // use v which involves calling get 
    return result; 
} 

и foo быть:

template<typename T> 
double foo (Vec<T>* v) 
{ 
return foo_template(v) 
} 

и пусть вывод аргумента сделать работу?

(Вы, вероятно, может избавиться от одной из функций, но я хотел, чтобы как можно ближе к оригиналу)

+0

Спасибо. Это было бы лучше. Однако, как я сказал в своем PS, у меня мало влияния на то, что точно передается 'foo'. Этот код окажется в библиотеке, которая вызывается другой программой (точнее, R). Эта программа не знает о 'Vec', поэтому я получаю некоторый общий указатель, который я должен использовать для правильного типа. –

+0

Обновленный вопрос, чтобы отразить предыдущий комментарий. –

0

Автоматическая отправка в C++ происходит с выполнения полиморфизма с помощью функции Virtua и static_type полиморфизма по mnas static_Cast, но вам нужно знать, какой тип для трансляции.

С различной конструкции, избегая void*, вы можете сделать следующее:

template<class Derived> 
class static_polimorphic {}; 

template<class A> 
A& upcast(static_polymorphic<A>& sa) 
{ return static_cast<A&>(sa); } 

template<class A> 
const A& upcast(const static_polymorphic<A>& sa) 
{ return static_cast<const A&>(sa); } 

Теперь вы классы shold быть как

class C1: public static_polymorphic<C1> 
{ 
.... 
}; 

class C2: public static_polymorphic<C2> 
{ 
.... 
}; 

Полиморфизм будет применяться в качестве

template<class A> 
void function(const static_polymorphic<A>& sa) 
{ 
    A& a = upcast(sa); 
    a.methods(); 
    ... 
} 

Другими словами, type больше представляет собой базовую переменную-член, а параметр базового шаблона.

Обратите внимание, что, будучи основанием, дифференцированным производным типом, общие функции не будут виртуальными. Вы можете полностью избежать полиморфизма, основанного на времени выполнения, если только вы не должны хранить другой объект, созданный во время выполнения, в один и тот же контейнер или коллекцию.

Для этой цели вы можете использовать вторую нерабочую базу с абстрактной виртуальной функцией как «пусковые установки» для одной из производных классов. (Может быть, лучше использовать полиномическую среду выполнения в качестве первой базы, чтобы упростить преобразование указателя времени выполнения, поскольку смещения не будет)

+0

Спасибо. Возможно, я не совсем понимаю, что делает ваш пример, но насколько я понимаю, это все равно не исключает оператора 'switch' или' if ... elseif', который у меня есть. При вызове 'function' мне все еще нужно знать тип. С вашим кодом я избавляюсь от BaseVec и не нуждаюсь в использовании 'reinterpret_cast' и/или' dynamic_cast'. Не могли бы вы объяснить, как будет выглядеть моя функция 'foo'? Благодарю. –

+0

Pont - это «tere», как и многие «функции» типа 'A'. Вам не нужно переключаться с тех пор, как каждый получает свой собственный. Вы не можете повторить свой конкретный случай, потому что у вас есть пустота * и тип информация, являющаяся значением времени выполнения. Пустота * убивает любую информацию о типе и не является хорошей основой для полиморфного дизайна. –

+0

Вы правы, было бы лучше избежать пустоты. Однако, как я уже говорил, у меня не так много влияние на то, что передается на 'foo', поскольку это определяется другой программой. Хотя, вы заставили меня думать о совершенно другом решении, которое может по-прежнему использовать ваш ответ, но избегает указателя void. Я попробую это, Подумайте об этом. –

0

Прежде всего, не используйте reinterpret_cast при преобразовании в/из указателя на полиморфный класс. Вы можете написать простой указатель обертку, который позволит вам использовать безопасный оператор литья static_cast:

template <class Type> 
class PointerWrapper 
{ 
public: 

    PointerWrapper(Type* object); 
    PointerWrapper& operator=(Type* object); 
    Type* operator->(); 

protected: 

    Type* object; 

}; 

template <class Type> 
PointerWrapper<Type>::PointerWrapper(Type* object) : 
    object(object) 
{ 
} 

template <class Type> 
PointerWrapper<Type>& PointerWrapper<Type>::operator=(Type* object) 
{ 
    this->object = object; 
} 

template <class Type> 
Type* PointerWrapper<Type>::operator->() 
{ 
    return object; 
} 

Теперь вы можете написать:

typedef PointerWrapper<BaseVec> BaseVecPointer; 

template<typename T> 
double foo(void* p) { 
    BaseVecPointer* vp = static_cast<BaseVecPointer*>(p); 
    // ... 
    // ... = (*vp)->get_double(...); 
    // ... 
    return result; 
} 

В этом коде возможности полиморфизма были использованы, то есть функция get_double называлась вместо звонка get.

Но если вы хотите назвать только get, не get_double, то есть вы хотите, чтобы вызвать функции шаблона с разными аргументами шаблона в зависимости от значения переменного времени выполнения, вы можете использовать следующий метод:

enum FooTypes 
{ 
    NoFooType = -1, 
    DoubleFooType = 0, 
    IntegerFooType = 1, 
    // ... 
    FooTypesCount 
}; 

template<FooTypes fooType> 
struct ChooseType 
{ 
    static 
    const FooTypes value = NoFooType; 

    typedef void Type; 
}; 

template<> 
struct ChooseType<DoubleFooType> 
{ 
    static 
    const FooTypes value = DoubleFooType; 

    typedef double Type; 
}; 

template<> 
struct ChooseType<IntegerFooType> 
{ 
    static 
    const FooTypes value = IntegerFooType; 

    typedef int Type; 
}; 

Здесь вы должны написать специализации шаблона класса ChooseType для всех возможных значений переменной type. После кода описывает функцию ChooseFoo, которая выбирает то, что специализация foo_template шаблона функции следует назвать:

typedef double (*FooFunction)(void*); 

template<FooTypes fooType> 
FooFunction ChooseFooImpl(int type) 
{ 
    if (type == fooType) 
    { 
     if (ChooseType<fooType>::value != NoFooType) 
     { 
      return foo_template<typename ChooseType<fooType>::Type>; 
     } 
     else 
     { 
      return NULL; 
     } 
    } 
    else 
    { 
     return ChooseFooImpl<(FooTypes)(fooType - 1)>(type); 
    } 
} 

template<> 
FooFunction ChooseFooImpl<NoFooType>(int type) 
{ 
    return NULL; 
} 

FooFunction ChooseFoo(int type) 
{ 
    return ChooseFooImpl<FooTypesCount>(type); 
} 

И это foo реализация функции:

double foo(void* p, int type) 
{ 
    FooFunction fooFunction = ChooseFoo(type); 

    if (fooFunction != NULL) 
    { 
     return fooFunction(p); 
    } 
    else 
    { 
     //unsupported type 
     // ... 
    } 
} 
+0

Спасибо. Это выглядит многообещающе. Я попробую. Это только на границе моего знания на C++, так что это может занять некоторое время. –

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