Разумный подход к уменьшению размера класса с помощью шаблонов? - PullRequest
0 голосов
/ 28 мая 2009

Дано: (Код приведен к разумному минимуму)

// MemberTypes

template
<
  typename SPEEDTYPE = float,
  typename SIZETYPE = float,
  typename ACCELERATIONTYPE = float
>
struct ParticleMemberTypes
{
  typedef typename SPEEDTYPE SpeedType;
  typedef typename SIZETYPE SizeType;
  typedef typename ACCELERATIONTYPE AccelerationType;
};

// Свойства

template <class T>
class PSpeed
{
public:
  inline const typename T::SpeedType&   GetSpeed() const  { return v; }
  inline void SetSpeed(const typename T::SpeedType& V)  { v = V; }
  const static bool hasSpeed = true;
private:
  typename T::SpeedType v;
};

template <class T> 
class PSize
{
public:
  inline const typename T::SizeType&    GetSize() const  { return v; }
  inline void SetSize(const typename T::SizeType& V)   { v = V; }
  const static bool hasSize = true;
private:
  typename T::SizeType v;
};

template <class T>
class PAcceleration
{
public:
  inline const typename T::AccelerationType& GetAcceleration() const  { return v; }
  inline void SetAcceleration(const typename T::AccelerationType& V)   { v = V; }
  const static bool hasAcceleration = true;
private:
  typename T::AccelerationType v;
};

// Пустая база и специализации

(Необходимо, чтобы каждый EmptyBase был отдельным типом, чтобы избежать наследования от одного и того же базового класса более одного раза)

template <typename P, typename T> struct EmptyBase {};
template <typename T> struct EmptyBase<PSpeed<T>, T>
{
  const static bool hasSpeed = false;
};
template <typename T> struct EmptyBase<PSize<T>, T>
{
  const static bool hasSize = false;
};
template <typename T> struct EmptyBase<PAcceleration<T>, T>
{
  const static bool hasAcceleration = false;
};

// Базовый шаблон выбора

template <bool ENABLE, typename P, typename T> struct EnableBase;
template <typename P, typename T> struct EnableBase<true, P, T>
{
  typedef P Type;
};
template <typename P, typename T> struct EnableBase<false, P, T>
{
  typedef EmptyBase<P, T> Type;
};

// Класс шаблонов частиц

template
<
  bool USE_SPEED = false,
  bool USE_SIZE = false,
  bool USE_ACCELERATION = false,
  typename T = ParticleMemberTypes<>
>
struct Particle :
  public EnableBase<USE_SPEED, PSpeed<T>, T>::Type,
  public EnableBase<USE_SIZE, PSize<T>, T>::Type,
  public EnableBase<USE_ACCELERATION, PAcceleration<T>, T>::Type
{
};

Теперь мы можем сделать:

using namespace std;

Particle<> p1;
Particle<true, true, true, ParticleMemberTypes<Vector3<double> > > p2;

cout << "p1: " << sizeof(p1) << endl;
cout << "p2: " << sizeof(p2) << endl;

Выход:

p1: 2
p1: 32

Итак, вот мои вопросы:

  • Это разумный подход к автоматическому уменьшению размера класса?
  • Если я наследую только от двух свойств, размер частиц равен 1, кроме того, размер увеличивается на единицу для каждого дополнительного EmptyBase, почему это так?
  • Есть ли здесь какие-нибудь шаблоны, идиомы и т. Д., Которые были бы полезны?

Планируется написать шаблоны для автоматизации обработки частиц на основе имеющихся свойств.

Я, вероятно, должен упомянуть, что эта система частиц, над которой я работаю, не "в реальном времени", будет иметь дело с огромным количеством частиц, и что я буду настраивать каждый рендеринг из C ++. Кроме того, это мой первый опыт использования шаблонов.

EDIT: В принципе, я выбрал шаблонный подход по двум причинам: одна из них - любопытство - просто узнать о шаблонах и изучить их использование. Вторая причина - скорость. Поскольку мне не нужно ничего менять во время выполнения, я подумал, что мог бы использовать шаблоны для устранения накладных расходов на виртуальные функции и неиспользуемые члены класса и т. Д.

Предполагаемое использование - это создать bazillion частиц абсолютно одинакового типа, а также обработать и визуализировать их так быстро, как только я смогу заставить код работать. :)

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

Ответы [ 3 ]

3 голосов
/ 28 мая 2009

Хм, во-первых, вы, кажется, катите много своих собственных вещей. Я бы посмотрел в Boost :: MPL , чтобы заменить, скажем, шаблон выбора базы, и наследовать по вектору с помощью Наследовать.

Во-вторых, вы используете "const static bool hasSpeed ​​= true;" много; В моем программировании я обычно предпочитаю черту с определением типа, подобную Boost :: type_traits . Вы можете использовать их для выбора функций для запуска во время компиляции. Вы можете избежать этого "EmptyBase".

template <typename T>
struct has_size;

template <bool speed, bool accel, typename T>
struct has_size< Particle<true, speed, accel, T> > : public true_type
{ };

template <bool speed, bool accel, typename T>
struct has_size< Particle<false, speed, accel, T> > : public false_type
{ };


// given a particle typedef SomeParticle_t

has_size<SomeParticle_T>::value

В-третьих, многое из того, что вы здесь делаете, зависит от того, каким вы хотите, чтобы ваше конечное использование было; Вы хотите, чтобы частицы были отдельными, типы, которые нельзя преобразовать друг в друга? Если вы пойдете по этому пути, почти каждая функция будет шаблоном, и вы можете использовать boost :: enable_if, чтобы отключить код, который не применяется к данной частице. Это будет потенциально очень быстро (так как во время компиляции происходит много работы), но у вас будут гигантские, трудно читаемые операторы ошибок (не очень доступные для новичка в шаблонах).

Другой, не шаблонный маршрут - наследование; вы должны определить набор виртуальных функций, в которых нуждается частица, а затем разбить их на классы, от которых вы наследуете. Из вашего кода не совсем понятно, что вы собираетесь делать с Частицей. Ошибки было бы легче понять, но код потенциально медленнее (большая часть работы перенесена во время выполнения и увеличит ваши объекты)

Вот пример кода, чтобы проиллюстрировать различия:

// Using templates
template <typename particle>
typename boost::enable_if< has_accel<particle>, typename particle::speed_type >::type
calculate_future_speed(const particle& t)
{ /* do your acceleration calculation */ }

template <typename particle>
typename boost::enable_if< boost::and_< has_speed<particle>,
                                       boost::not_< has_accel<particle> >,
                         typename particle::speed_type >::type
calculate_future_speed(const particle& t)
{ /* no acceleration, so no calculation! just return the speed*/ }

template <typename particle>
typename boost::enable_if< boost::not_< has_speed<particle>,
                         typename particle::speed_type >::type
calculate_future_speed(const particle& t)
{ return 0; /* Has no speed, and no acceleration */ }
2 голосов
/ 28 мая 2009

Вам известно, что типы шаблонов с разными параметрами разные? То есть в вашем коде p1 и p2 относятся к разным типам и поэтому не могут храниться в одной коллекции, назначаться друг другу и т. Д. Конечно, ваше приложение может не требовать такой эквивалентности.

1 голос
/ 28 мая 2009

Во-первых, вы используете USE_SIZE дважды в определении шаблона для Particle.

Вы также можете изучить использование шаблона flyweight, в зависимости от повторяемости данных. http://www.boost.org/doc/libs/1_39_0/libs/flyweight/doc/tutorial/index.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...