Шаблон для работы с массивом разных типов
От: glut  
Дата: 24.10.03 08:34
Оценка:
Всем привет!

Есть задачка, которую я уже решил, но полистав книгу Александреску,
ришил что я не понимаю в проектировании ничего вообще. Есть решение лучше чем мое?

Итак:

Есть *фиксированое* число типов, скажем 3 (на самом деле их 15). Нужен класс

class CArrayClass
{
   InitArray ( int NumItems, ТИП* pArray ); // создает массив
   RemoveItem( ТИП Item  ); // удалить элемент массива со значением Item

   ТИП* m_pArray;
};


Вот три типа которыми этот класс должен уметь оперирировать:

typedef char STR255[255];

typedef struct {
   DWORD l,r,b,d;
} RECT;

typedef unsigned int UINT;


Cоздание простого шаблона не помогает по причине того, что копировать STR255 нужно будет не
m_pArray[i]=pArray[i]
а
*m_pArray[i]=*pArray[i]

не говоря уже о сравнении в методе RemoveItem.

Cоздание базового темплейта с виртуальными функциями

void CopyItem(TYPE* pdst, TYPE src) 
BOOL СompareItems(TYPE item1, TYPE item2)


переопределенными в темлейтах TArrayValue и TArrayPointer не очень хорошо, т.к. сгенерится
много классов (ведь так? =), что нежелательно из-за большого обема.

Мое решение:

typedef unsigned char* PBYTE;

class CArrayBase
{
protected:
   CArrayBase();   
   virtual ~CArrayBase;

   InitArray ( int NumItems, PBYTE* pArray ); // создает массив
   RemoveItem( PBYTE Item ); // удалить элемент массива со значением Item

   virtual void CopyItem(PBYTE* pdst, PBYTE src) {};
   virtual BOOL CompareItems(PBYTE item1, PBYTE item2) {return TRUE;};

   PBYTE* m_pArray;

   int m_TypeSize; // реальный размер того что передается через PBYTE 
                   // в моей реальной задаче это в любом случае надо, для других целей..
};  


template <class TYPE, int TYPE_SIZE>
class TArrayPointer: public CArrayBase // темплейт для ряботы с указателями (STR255 и RECT*)
{
public:
   CArrayBase() {m_TypeSize=TYPE_SIZE;}
   virtual void CopyItem(PBYTE* pdst, PBYTE src) 
      { **((TYPE*)pdst)=*(*(TYPE*)&src) };
   virtual BOOL CompareItems(PBYTE item1, PBYTE item2) 
      { return _strnicmp((LPCSTR)item1,(LPCSTR)item2,__TYPE_SIZE); };
};

// ну и по такому же принципу темплэйт TArrayValue для работы с UINT


Пожалуйста, расскажите как такую задачу спроектировать лучше?
Re: Шаблон для работы с массивом разных типов
От: glut  
Дата: 24.10.03 10:16
Оценка:
Исправил/Дополнил...

typedef unsigned char* PBYTE;

class CArrayBase
{
protected:
   CArrayBase();   
   virtual ~CArrayBase;

public:
   void InitArray ( int NumItems, PBYTE* pArray ); // создает массив
   void RemoveItem( PBYTE Item ); // удалить элемент массива со значением Item

protected:
   virtual void CopyItem(PBYTE* pdst, PBYTE src) {};
   virtual BOOL CompareItems(PBYTE item1, PBYTE item2) {return TRUE;};

   PBYTE* m_pArray;

   int m_TypeSize; // реальный размер того что передается через PBYTE 
                   // в моей реальной задаче это в любом случае надо, для других целей..
};  


template <class TYPE, int TYPE_SIZE>
class TArrayPointer: public CArrayBase // темплейт для ряботы с указателями (STR255 и RECT*)
{
public:
   CArrayBase() {m_TypeSize=TYPE_SIZE;}

   void InitArray ( int NumItems, TYPE* pArray )
      { CArrayBase::RemoveItem((PBYTE*) pArray); }
   void RemoveItem( TYPE Item ); 
      { CArrayBase::RemoveItem((PBYTE) Item);

protected:
   virtual void CopyItem(PBYTE* pdst, PBYTE src) 
      { **((TYPE*)pdst)=*(*(TYPE*)&src) };
   virtual BOOL CompareItems(PBYTE item1, PBYTE item2) 
      { return _strnicmp((LPCSTR)item1,(LPCSTR)item2,__TYPE_SIZE); };
};

// ну и по такому же принципу темплэйт TArrayValue для работы с UINT
Re: Шаблон для работы с массивом разных типов
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.10.03 10:40
Оценка:
Здравствуйте, glut, Вы писали:

Сделай один шаблон, класса который будет вызывать внешнюю функцию void ::Copy(T &dest, const T&source).
Определи шаблонную функцию
template <class T>
inline void ::Copy(T& dest, const T& source)
{
   dest = source; // используем встроенный оператор присваивания;
}

специализируй ее для своего CHAR255:
inline void ::Copy(CHAR255& dest, const CHAR255& source)
{
   for (int i=0; i<255; i++) // лучше пользоваться чем-то более оптимальным, но это не важно
         dest[i] = source[i]; // поэлементное копирование
}

тогда при подстановке в шаблон твоего массива CHAR255 автоматически будет выбрана более специализированная версия ::Copy.
Такой подход потребует специализировать Copy для всех случаев, когда тривиального опреатора = недостаточно. Я не гуру в плюсах, так что такой вариант может не сработать:
template <class pT>
inline void ::Copy((pT[])& dest, const (pT[])& source)
{
   for (int i=0; i<255; i++) 
         dest[i] = source[i]; // поэлементное копирование
}

т.к. компилер может не понять, что к char[] эта версия copy подходит лучше, чем скалярная.
... << RSDN@Home 1.1 beta 2 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Шаблон для работы с массивом разных типов
От: glut  
Дата: 24.10.03 10:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

[skip]

S>специализируй ее для своего CHAR255:

S>
S>inline void ::Copy(CHAR255& dest, const CHAR255& source)
S>{
[skip]
S>}
S>

S>тогда при подстановке в шаблон твоего массива CHAR255 автоматически будет выбрана более специализированная версия ::Copy.

S>Такой подход потребует специализировать Copy для всех случаев, когда тривиального опреатора = недостаточно.


Вот это плохо =((

S>
S>template <class pT>
S>inline void ::Copy((pT[])& dest, const (pT[])& source)
S>{
S>   for (int i=0; i<255; i++) 
S>         dest[i] = source[i]; // поэлементное копирование
S>}
S>


это вообще не компилится =(

А с базовым классом и шаблоном переопределяющим InitArray и Remove так, что б они принимали TYPE, ты согласен?
Re: Шаблон для работы с массивом разных типов
От: alexanderfedin США http://alexander-fedin.pixels.com/
Дата: 24.10.03 13:30
Оценка:
сделай:
template <typename _Ty, typename _Traits = DefaultTraits>
class CArrayClass
{
    friend class _Traits;
    ...

    static void copy(_Ty *pDst, const _Ty &src)
    {
        _Traits::copy(pDst, src);
    }

    static bool compare(const _Ty &lhs, const _Ty &rhs)
    {
        return _Traits::compare(lhs, rhs);
    }
};

никакой виртуальности, работает быстро.
typedef unsigned char* PBYTE;

class CArrayBase
{
protected:
CArrayBase();
virtual ~CArrayBase;

InitArray ( int NumItems, PBYTE* pArray ); // создает массив
RemoveItem( PBYTE Item ); // удалить элемент массива со значением Item

virtual void CopyItem(PBYTE* pdst, PBYTE src) {};
virtual BOOL CompareItems(PBYTE item1, PBYTE item2) {return TRUE;};

PBYTE* m_pArray;

int m_TypeSize; // реальный размер того что передается через PBYTE
// в моей реальной задаче это в любом случае надо, для других целей..
};


template <class TYPE, int TYPE_SIZE>
class TArrayPointer: public CArrayBase // темплейт для ряботы с указателями (STR255 и RECT*)
{
public:
CArrayBase() {m_TypeSize=TYPE_SIZE;}
virtual void CopyItem(PBYTE* pdst, PBYTE src)
{ **((TYPE*)pdst)=*(*(TYPE*)&src) };
virtual BOOL CompareItems(PBYTE item1, PBYTE item2)
{ return _strnicmp((LPCSTR)item1,(LPCSTR)item2,__TYPE_SIZE); };
};

// ну и по такому же принципу темплэйт TArrayValue для работы с UINT


Пожалуйста, расскажите как такую задачу спроектировать лучше?

Шаблон для работы с массивом разных типов Оценить
Posted via RSDN NNTP Server 1.8 beta
Respectfully,
Alexander Fedin.
Re[2]: Шаблон для работы с массивом разных типов
От: glut  
Дата: 24.10.03 14:05
Оценка:
Здравствуйте, alexanderfedin, Вы писали:

A>сделай:

A>
A>template <typename _Ty, typename _Traits = DefaultTraits>
A>class CArrayClass
A>{
A>    friend class _Traits;
A>    ...

A>    static void copy(_Ty *pDst, const _Ty &src)
A>    {
A>        _Traits::copy(pDst, src);
A>    }

A>    static bool compare(const _Ty &lhs, const _Ty &rhs)
A>    {
A>        return _Traits::compare(lhs, rhs);
A>    }
A>};
A>

A>никакой виртуальности, работает быстро.

Спасибо! Но я ничего не понял =))

т.е. обойтись совсем без базового класса (тот, который оперирует PBYTE)? Тогда будет 15 классов (типов же 15), мне это не нравится =((

а _Traits это что? интерфейс? и два (DefaultTraits, PointerTraints) класса с ним?

Пожалуйста, можно подробней?
Re: Шаблон для работы с массивом разных типов
От: tonal www@promsoft.ru
Дата: 25.10.03 15:33
Оценка:
G>Есть *фиксированое* число типов, скажем 3 (на самом деле их 15). Нужен класс

G>
G>class CArrayClass
G>{
G>   InitArray ( int NumItems, ТИП* pArray ); // создает массив
G>   RemoveItem( ТИП Item  ); // удалить элемент массива со значением Item

G>   ТИП* m_pArray;
G>};
G>


G>Вот три типа которыми этот класс должен уметь оперирировать:


G>
G>typedef char STR255[255];

G>typedef struct {
G>   DWORD l,r,b,d;
G>} RECT;

G>typedef unsigned int UINT;
G>


G>Cоздание простого шаблона не помогает по причине того, что копировать STR255 нужно будет не

G>
m_pArray[i]=pArray[i]
а
*m_pArray[i]=*pArray[i]

G>не говоря уже о сравнении в методе RemoveItem.
Почему бы контейнеру не оперировать не странным типом
typedef char STR255[255];

А скажем
struct STR255_t {char val[255];};

Который с лёгкостью примет любой шаблонный контейнер?.
Эту обёртачку можно сделать шаблоном для масивов любого типа фиксированной
длинны:
template <class T, size_t size>
struct array_struct_t {
  T val[size];
};

И по вкусу определяем для ней (обёрточки) различные методы,
Например:
template <class T, size_t size>
bool operator<(const array_struct_t<T, size>& a, const array_struct_t<T, size>& b) {
  return std::lexicographical_compare(a.val, a.val + size, b.val, b.val + size);
}

Можно определить преобразования, если надо. ;-в
... << RSDN@Home 1.1 beta 2 >>
Re[2]: Шаблон для работы с массивом разных типов
От: glut  
Дата: 26.10.03 08:28
Оценка:
Здравствуйте, tonal, Вы писали:

[skip]
T>Почему бы контейнеру не оперировать не странным типом
typedef char STR255[255];

T>А скажем
T>
T>struct STR255_t {char val[255];};
T>

T>Который с лёгкостью примет любой шаблонный контейнер?.
T>Эту обёртачку можно сделать шаблоном для масивов любого типа фиксированной
T>длинны:
T>
T>template <class T, size_t size>
T>struct array_struct_t {
T>  T val[size];
T>};
T>

T>И по вкусу определяем для ней (обёрточки) различные методы,
T>Например:
T>
T>template <class T, size_t size>
T>bool operator<(const array_struct_t<T, size>& a, const array_struct_t<T, size>& b) {
T>  return std::lexicographical_compare(a.val, a.val + size, b.val, b.val + size);
T>}
T>

T>Можно определить преобразования, если надо. ;-в

В любом случае мне нужно будет передавать этот T в базовый класс, и в итоге опять получится 15 классов =(( потому что надо реализовать T& operator [](int) и т.п.

Но вообще идея интересная, спасибо!
Re: Шаблон для работы с массивом разных типов
От: dad  
Дата: 27.10.03 10:13
Оценка:
G>Есть задачка, которую я уже решил, но полистав книгу Александреску,

не знаю, как пишет Александровский, но я бы сделал класс обертку (если нужен класс хрянящий !разные одновременно , и нельзя наследовать от одного )


class ArrayValue
{
enum ValueType
{
....
} _valueType;
ArrayValue();

public:
ArrayValue(Type1&);
ArrayValue(Type2&);
ArrayValue(Type3&);

bool operator == (Type3&);
bool operator == (Type2&);
bool operator == (Type3&);

//etc..
}

и массив который работает с типом ArrayValue.

вариант два если один класс но значения одновременно могут храниться только дного типа:

обертку — шаблон


template<class _T>
class ArrayValue
{
_T* _value;
public:
_T* operator ->(){ return _value; }
bool operator == (const ArrayValue<_T>& src)
{
return *_value == src._value;
}
};

template <_class _T>
class Array
{
public:
void insertItem(_T&);
};

в этом случае (да и впервом) клиенту не надо знать о наличии класса ArrayValue, главное чтобы твои типы были оборудованы операторами сравнения, копирования и т.д.
В случае номер 1 — даже этого не обязательно все делает класс ArrayValue
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Re[3]: Шаблон для работы с массивом разных типов
От: alexanderfedin США http://alexander-fedin.pixels.com/
Дата: 29.10.03 08:05
Оценка:
Здравствуйте, glut, Вы писали:

>>т.е. обойтись совсем без базового класса (тот, который оперирует PBYTE)?

>>Тогда будет 15 классов (типов же 15), мне это не нравится =((

а у тебя настолько разные типы, с которыми нужно оперировать?
_Traits — это класс настройки на конкретные особенности типа, то есть здесь
он определяет операции по копированию и сравнению экземпляров.
В общем случае это будет
template <typename _Ty>
struct _DefaultTraits
{
    static void copy(_Ty *dst, const _Ty &src)
    {
        *dst = src;
    }

    static bool compare(const _Ty &lhs, const _Ty &rhs)
    {
        return (lhs == rhs);
    }
};

Для примитивных типов операции копирования и сравнения определены языком.
Для типа массива напиши что-то типа:
template <typename _Ty>
struct _ArrayTraits
{
    static void copy(_Ty *dst, const _Ty &src)
    {
        for (int i = 0; i < sizeof(src)/sizeof(src[0]); ++i) (*dst)[i] =
src[i];
    }

    static bool compare(const _Ty &lhs, const _Ty &rhs)
    {
        for (int i = 0; i < _size; ++i)
            if (!(dst[i] == src[i])) return false;
        return true;
    }
};

для своих типов определи операции сравнения и присвоения:
class MyClass
{
   public:
        MyClass &operator=(const MyClass &src) {...}
        friend bool operator==(const MyClass &lhs, const MyClass &rhs) {...}
};

используй так:
template <typename _Ty, typename _Traits = _DefaultTraits>
class CArrayClass
{
    friend class _Traits;

    static void copy(_Ty *dst, const _Ty &src)
    {
        _Traits::copy(dst, src);
    }

    static bool compare(const _Ty &lhs, const _Ty &rhs)
    {
        return _Traits::compare(lhs, rhs);
    }
};

typedef char STR255[255];
typedef struct {
    DWORD l, r, b, d;
} RECT;
bool operator==(const RECT &lhs, const RECT &lhs) { return (lhs.l == rhs.l
&& lhs.r == rhs.r && lhs.b == rhs.b && lhs.d == rhs.d); }
typedef unsigned int UINT;

void main()
{
    typedef CArrayClass<STR255, _ArrayTraits<STR255> >  _ArrayUtilitySTR255;
    typedef CArrayClass<RECT, _DefaultTraits>  _ArrayUtilityRECT;
    typedef CArrayClass<UINT, _DefaultTraits>  _ArrayUtilityUINT;

    STR255 s1 = "hghghjjhg", s2 = "aaaaaa";
    if (!_ArrayUtilitySTR255::compare(s1, s2))
        _ArrayUtilitySTR255::copy(s1, s2);

    RECT r1 = {0, 1, 2, 3}, r2 = {3, 4, 5, 6};
    if (!_ArrayUtilityRECT::compare(r1, r2))
        _ArrayUtilityRECT::copy(r1, r2);

    UINT ui1 = 0, ui2 = 2;
    if (!_ArrayUtilityUINT::compare(ui1, ui2))
        _ArrayUtilityUINT::copy(ui1, ui2);
}


если тебе нужна независимость класса CArrayClass от обрабатываемых типов,
тогда примени метафункции — будешь по-прежнему обходиться без накладных
расходов на вызов виртуальных функций, что с поддержкой компилятором inline
подстановки даст тебе быстродействие прямого использования необходимых
алгоритмов для соотвествующих типов.

С уважением,
Александр Федин (AlexanderFedin@iname.com)
Respectfully,
Alexander Fedin.
Re[4]: Шаблон для работы с массивом разных типов
От: glut  
Дата: 30.10.03 13:59
Оценка:
Здравствуйте, alexanderfedin, Вы писали:

>>>т.е. обойтись совсем без базового класса (тот, который оперирует PBYTE)?

>>>Тогда будет 15 классов (типов же 15), мне это не нравится =((

A>а у тебя настолько разные типы, с которыми нужно оперировать?


нет, просто я подумал что _DefaultTraits<int> и _DefaultTraits<char> это будет два разных типа, и соответственно TArrayBase<int, _DefaultTraits<int> > и СArrayСlass<char, _DefaultTraits<char> > будет тоже два разных класса и в итоге получится 15 (там int, char, short и т.п) классов.. Или я не прав? если не прав, то почему?

A>_Traits — это класс настройки на конкретные особенности типа, то есть здесь

A>он определяет операции по копированию и сравнению экземпляров.

A>В общем случае это будет

A>
A>template <typename _Ty>
A>struct _DefaultTraits
A>{
A>    static void copy(_Ty *dst, const _Ty &src) {SKIP}
A>    static bool compare(const _Ty &lhs, const _Ty &rhs) {SKIP}
A>};
A>

A>Для примитивных типов операции копирования и сравнения определены языком.
A>Для типа массива напиши что-то типа:
A>используй так:
A>[code]
A>template <typename _Ty, typename _Traits = _DefaultTraits>
A>class CArrayClass
A>{
A> friend class _Traits;

A> static void copy(_Ty *dst, const _Ty &src)

A> {
A> _Traits::copy(dst, src);
A> }

A> static bool compare(const _Ty &lhs, const _Ty &rhs)

A> {
A> return _Traits::compare(lhs, rhs);
A> }
A>};

А Почему _DefaultTraits без <> ? А разве CArrayClass<int> и CArrayClass<char> не сгенерят два разных класса?

A>если тебе нужна независимость класса CArrayClass от обрабатываемых типов,

A>тогда примени метафункции — будешь по-прежнему обходиться без накладных
A>расходов на вызов виртуальных функций, что с поддержкой компилятором inline
A>подстановки даст тебе быстродействие прямого использования необходимых
A>алгоритмов для соотвествующих типов.

Метафункции это кто?

Большое спасибо за все эти указания, но боюсь я все же тупой или просто чего-то другого не понимаю. Я был бы очень признателен если б мне показали, как TypeTraits и т.п. применить конкретно к моей задаче, т.е. код(определения классов) в котором есть класс c InitArray и RemoveItem и который умеет оперировать тремя описаными мной данными.

В моем решении, помимо виртуальных функций мне не нравится, что для того чтобы не делать 15 классов с RemoveItem и InitArray приходится оперировать PBYTE. Без PBYTE можно обойтись?

Заранее спасибо.
Re[5]: Шаблон для работы с массивом разных типов
От: glut  
Дата: 30.10.03 14:07
Оценка:
поправил..

G>нет, просто я подумал что _DefaultTraits<int> и _DefaultTraits<char> это будет два разных типа, и соответственно CArrayClass<int, _DefaultTraits<int> > и СArrayСlass<char, _DefaultTraits<char> > будет тоже два разных класса и в итоге получится 15 (там int, char, short и т.п) классов.. Или я не прав? если не прав, то почему?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.