Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: STL контейнер + GUID  (Прочитано 23925 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
WDMclient
Гость
« : 03-03-2010 09:57 » 

Не могу закинуть GUID  в контейнер MAP (STL).

так нормально:

std::map< IUnknown* ,  GUID > kot;
   
kot.insert(std::map< IUnknown* , GUID  >::value_type(  d  , IID_IKot ) );



а вот с таким образом не проходит:

       std::map< GUID  ,  IUnknown*> kot;

kot.insert(std::map<  GUID  ,  IUnknown* >::value_type( IID_IKot  ,  d ) );


Может кто знает и как енто обойти?
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #1 : 03-03-2010 10:22 » 

для GUID нет функции меньше, вот и не проходит, определи ее сам и все у тебя получится

имхо эта тема должна быть в разделе с++
« Последнее редактирование: 03-03-2010 10:29 от lapulya » Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 03-03-2010 10:43 » 

   
kot.insert(std::map< IUnknown* , GUID  >::value_type(  d  , IID_IKot ) );


а имхо это проще так
Код:
 IUnknown* p=...;
 GUID g;
kot[p]=g;
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #3 : 03-03-2010 14:56 » 

Алексей1153++, надо то наоборот, чтобы ключом был GUID. Так как ты предлагаешь, он и сам смог, только мапом.
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #4 : 03-03-2010 15:36 » 

тю )

Код:
IUnknown* p=...;
 GUID1153 g;
kot[g]=p;


(писал когда то)
Код:
class GUID1153:GUID
{

//оператор для
operator _GUID* ()
{
return (_GUID*)this;
}

public:

//оператор для
operator _GUID* () const
{
return (_GUID*)this;
}


GUID1153(const char* p)
{
SetGUIDfromString(p);
}

GUID1153(const GUID1153* pg=0)
{
#ifdef _DEBUG
if(sizeof(*this)!=16)throw 0;
#endif

if(pg)
{
::memmove(this,pg,min(sizeof(*this),sizeof(*pg)) );
}
else
{
::memset(this,0,sizeof(*this));
}
}

void NewGuid()
{
::CoCreateGuid(*this);
}


bool IsNull() const
{
return *this==GUID1153();
}


CString GetGuidLikeString() const
{
CString text;
const BYTE* p2=(BYTE*)this;

text.Format(
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
"%02X"
,*( 0+p2)
,*( 1+p2)
,*( 2+p2)
,*( 3+p2)
,*( 4+p2)
,*( 5+p2)
,*( 6+p2)
,*( 7+p2)
,*( 8+p2)
,*( 9+p2)
,*(10+p2)
,*(11+p2)
,*(12+p2)
,*(13+p2)
,*(14+p2)
,*(15+p2)
);
return text;
}

bool SetGUIDfromString(const char* pText)
{
(*this)=GUID1153();

if(::strlen(pText)*2<sizeof(*this))return false;

const char* p1=pText;
BYTE* p2=(BYTE*)this;

for(int i=0;i<sizeof(*this);i++,p2++,p1+=2)
{
*p2 = st_atoByteXX(p1);
}

return true;
}

bool (operator ==)(const GUID1153& g2) const
{
return(0==::memcmp(this,&g2, min(sizeof(*this),sizeof(g2) )));
}

bool (operator !=)(const GUID1153& g2) const
{
return !( (*this) == g2);
}

};

« Последнее редактирование: 03-03-2010 15:43 от Алексей1153++ » Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #5 : 03-03-2010 15:40 » 

+
Код:
	bool (operator <)(const GUID1153& g2) const
{
return(0<::memcmp(this,&g2, min(sizeof(*this),sizeof(g2) )));
}
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 03-03-2010 15:47 » 

а, ещё одна функция там у меня, вот код (скажете - громоздко, но я это делал для скорости - мне надо было. Если хочете - переделайте по вкусу )

Код:
//перевод строки "XX" в BYTE
static BYTE st_atoByteXX(const char* pText)
{
BYTE byRes=0;
if(!pText)return byRes;

if(pText)
{
switch(*pText)
{
case 0:
default :
{
pText=0;//конец просмотра строки
}
break;

case '0':byRes|=0x00;break;
case '1':byRes|=0x10;break;
case '2':byRes|=0x20;break;
case '3':byRes|=0x30;break;
case '4':byRes|=0x40;break;
case '5':byRes|=0x50;break;
case '6':byRes|=0x60;break;
case '7':byRes|=0x70;break;
case '8':byRes|=0x80;break;
case '9':byRes|=0x90;break;

case 'a':
case 'A':byRes|=0xa0;break;

case 'b':
case 'B':byRes|=0xb0;break;

case 'c':
case 'C':byRes|=0xc0;break;

case 'd':
case 'D':byRes|=0xd0;break;

case 'e':
case 'E':byRes|=0xe0;break;

case 'f':
case 'F':byRes|=0xf0;break;
}
}

if(pText)
{
pText++;

switch(*pText)
{
case 0:
default :
{
pText=0;//конец просмотра строки
}
break;

case '0':byRes|=0x00;break;
case '1':byRes|=0x01;break;
case '2':byRes|=0x02;break;
case '3':byRes|=0x03;break;
case '4':byRes|=0x04;break;
case '5':byRes|=0x05;break;
case '6':byRes|=0x06;break;
case '7':byRes|=0x07;break;
case '8':byRes|=0x08;break;
case '9':byRes|=0x09;break;

case 'a':
case 'A':byRes|=0x0a;break;

case 'b':
case 'B':byRes|=0x0b;break;

case 'c':
case 'C':byRes|=0x0c;break;

case 'd':
case 'D':byRes|=0x0d;break;

case 'e':
case 'E':byRes|=0x0e;break;

case 'f':
case 'F':byRes|=0x0f;break;
}
}

return byRes;
}
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #7 : 03-03-2010 22:01 » 

Это вот такие два свича для скорости??? И как, быстро работало? Или ты замедлить хотел?
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 04-03-2010 04:06 » 

свич работает отменно Улыбаюсь

хотя, тестов я не проводил, но это явно быстрее, чем парсить строку
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #9 : 04-03-2010 04:19 » 

тест, правда не очень чистый, поскольку применил atoi (для хекса вроде нет готовой парсилки)
Код:
static BYTE st_atoByteXX_parse(const char* pText)
{
return(BYTE)atoi(pText);//вернёт неправильное значение!
}


Код:
	const char* p="55";
BYTE by=0;

int i;
int imax=10000000;

//выполнилось за 1 секунду
for(i=0;i<imax;i++)
{
by=st_atoByteXX(p);
}

//выполнилось за 4 секунды
for(i=0;i<imax;i++)
{
by=st_atoByteXX_parse(p);
}

int iii=1;


хотя, на таком количестве итераций разница только ощутилась - может и стОит заменить на парсилку )
« Последнее редактирование: 04-03-2010 04:22 от Алексей1153++ » Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #10 : 04-03-2010 20:54 » 

надо воспользоваться тем, что символы с 0 по 9, с "a" по "z" и с "A" по "Z" идут подряд в таблице символов
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #11 : 04-03-2010 21:08 » 

lapulya, Улыбаюсь так дольше будет. Быстрее свича зверя нет
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #12 : 05-03-2010 12:04 » 

lapulya, да я тоже не поверил, тест это подтвердил, правда оптимизация была выключена. при включенной оптимизатор выкидывал циклы к чёртям собачьим.
Записан

Странно всё это....
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 05-03-2010 12:49 » 

при включенной оптимизатор выкидывал циклы к чёртям собачьим.
может, надо было by использовать где-то - например, сохранять последовательно полученные значения в вектор
Записан

WDMclient
Гость
« Ответ #14 : 06-03-2010 13:17 » 

Кстати ,  хотел спасибца сказать , а то я
по уши залез в отладку , не сразу вылез.

Исходники были как раз.....
в общем мне было чему поучиться  %=))  

(Вараинт достаточный для работы с итераторами:)

class mGUID:public _GUID
{
   
public:
   mGUID(){;}
   mGUID( _GUID guid):_GUID(guid){;} 

   //-----------------------------
   bool operator<(const mGUID & G1 ) const
   {
      return memcmp((byte*)this,(byte*)&G1,sizeof(_GUID))<0 ? true : false;
   }
   //-----------------------------
};


THANKS!!
« Последнее редактирование: 07-03-2010 02:40 от WDMclient » Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #15 : 07-03-2010 14:24 » 

да я и не предлагал делать никаких циклов, я просто хотел сказать что достаточно было сделать примерно так (для интервала от 0 до 9)
byRes |= (*pText - k) * n;
где к - константа надо посмотреть чему она там равна в таблице...
где n - константа чтобы нужный шаг задать...
что-то в этом роде, вот это было бы быстро, а свитч... ппц какой медленный...
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #16 : 07-03-2010 14:27 » 

lapulya, давай, покажи не "примерно так" , а "точно так" - и сравним. Я что, против ? Улыбаюсь
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #17 : 07-03-2010 14:31 » 

нееее.... мне писать в лом )))), я ж не заставляю, думай как хочешь... можно просто поразмышлять, как работает свитч, например при  *pText == '9'? Я только идею предложил, которая, как мне кажется, много (в относительном сравнении) быстрее, чем свитч.
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #18 : 07-03-2010 14:36 » 

lapulya, кажется - надо креститься Улыбаюсь
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #19 : 07-03-2010 14:40 » new

не, не надо, но так я не увидел ответа на то как работает свитч при 9... а работает он, насколько я помню последовательным перебором (сравниваем с 0, не канает? сравниваем с 1, не канает? сравниваем с 2 иииии т.д.). правильно?
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #20 : 07-03-2010 14:43 » 

Код:
//перевод строки "XX" в BYTE
BYTE st_atoByteXX(const char* pText)
{
00502210  push        ebp 
00502211  mov         ebp,esp
00502213  sub         esp,0D0h
00502219  push        ebx 
0050221A  push        esi 
0050221B  push        edi 
0050221C  lea         edi,[ebp-0D0h]
00502222  mov         ecx,34h
00502227  mov         eax,0CCCCCCCCh
0050222C  rep stos    dword ptr es:[edi]
BYTE byRes=0;
0050222E  mov         byte ptr [byRes],0
if(!pText)return byRes;
00502232  cmp         dword ptr [pText],0
00502236  jne         st_atoByteXX+30h (502240h)
00502238  mov         al,byte ptr [byRes]
0050223B  jmp         $LN1+0Dh (50248Dh)

if(pText)
00502240  cmp         dword ptr [pText],0
00502244  je          $LN21+0Ch (50236Ah)
{
switch(*pText)
0050224A  mov         eax,dword ptr [pText]
0050224D  movsx       ecx,byte ptr [eax]
00502250  mov         dword ptr [ebp-0D0h],ecx
00502256  mov         edx,dword ptr [ebp-0D0h]
0050225C  sub         edx,30h
0050225F  mov         dword ptr [ebp-0D0h],edx
00502265  cmp         dword ptr [ebp-0D0h],36h
0050226C  ja          st_atoByteXX+72h (502282h)
0050226E  mov         eax,dword ptr [ebp-0D0h]
00502274  movzx       ecx,byte ptr  (5024D8h)[eax]
0050227B  jmp         dword ptr  (502494h)[ecx*4]
{
case 0:
default :
{
pText=0;//конец просмотра строки
00502282  mov         dword ptr [pText],0
}
break;
00502289  jmp         $LN21+0Ch (50236Ah)

case '0':byRes|=0x00;break;
0050228E  mov         al,byte ptr [byRes]
00502291  mov         byte ptr [byRes],al
00502294  jmp         $LN21+0Ch (50236Ah)
case '1':byRes|=0x10;break;
00502299  movzx       eax,byte ptr [byRes]
0050229D  or          eax,10h
005022A0  mov         byte ptr [byRes],al
005022A3  jmp         $LN21+0Ch (50236Ah)
case '2':byRes|=0x20;break;
005022A8  movzx       eax,byte ptr [byRes]
005022AC  or          eax,20h
005022AF  mov         byte ptr [byRes],al
005022B2  jmp         $LN21+0Ch (50236Ah)
case '3':byRes|=0x30;break;
005022B7  movzx       eax,byte ptr [byRes]
005022BB  or          eax,30h
005022BE  mov         byte ptr [byRes],al
005022C1  jmp         $LN21+0Ch (50236Ah)
case '4':byRes|=0x40;break;
005022C6  movzx       eax,byte ptr [byRes]
005022CA  or          eax,40h
005022CD  mov         byte ptr [byRes],al
005022D0  jmp         $LN21+0Ch (50236Ah)
case '5':byRes|=0x50;break;
005022D5  movzx       eax,byte ptr [byRes]
005022D9  or          eax,50h
005022DC  mov         byte ptr [byRes],al
005022DF  jmp         $LN21+0Ch (50236Ah)
case '6':byRes|=0x60;break;
005022E4  movzx       eax,byte ptr [byRes]
005022E8  or          eax,60h
005022EB  mov         byte ptr [byRes],al
005022EE  jmp         $LN21+0Ch (50236Ah)
case '7':byRes|=0x70;break;
005022F0  movzx       eax,byte ptr [byRes]
005022F4  or          eax,70h
005022F7  mov         byte ptr [byRes],al
005022FA  jmp         $LN21+0Ch (50236Ah)
case '8':byRes|=0x80;break;
005022FC  movzx       eax,byte ptr [byRes]
00502300  or          eax,80h
00502305  mov         byte ptr [byRes],al
00502308  jmp         $LN21+0Ch (50236Ah)
case '9':byRes|=0x90;break;
0050230A  movzx       eax,byte ptr [byRes]
0050230E  or          eax,90h
00502313  mov         byte ptr [byRes],al
00502316  jmp         $LN21+0Ch (50236Ah)

case 'a':
case 'A':byRes|=0xa0;break;
00502318  movzx       eax,byte ptr [byRes]
0050231C  or          eax,0A0h
00502321  mov         byte ptr [byRes],al
00502324  jmp         $LN21+0Ch (50236Ah)

case 'b':
case 'B':byRes|=0xb0;break;
00502326  movzx       eax,byte ptr [byRes]
0050232A  or          eax,0B0h
0050232F  mov         byte ptr [byRes],al
00502332  jmp         $LN21+0Ch (50236Ah)

case 'c':
case 'C':byRes|=0xc0;break;
00502334  movzx       eax,byte ptr [byRes]
00502338  or          eax,0C0h
0050233D  mov         byte ptr [byRes],al
00502340  jmp         $LN21+0Ch (50236Ah)

case 'd':
case 'D':byRes|=0xd0;break;
00502342  movzx       eax,byte ptr [byRes]
00502346  or          eax,0D0h
0050234B  mov         byte ptr [byRes],al
0050234E  jmp         $LN21+0Ch (50236Ah)

case 'e':
case 'E':byRes|=0xe0;break;
00502350  movzx       eax,byte ptr [byRes]
00502354  or          eax,0E0h
00502359  mov         byte ptr [byRes],al
0050235C  jmp         $LN21+0Ch (50236Ah)

case 'f':
case 'F':byRes|=0xf0;break;
0050235E  movzx       eax,byte ptr [byRes]
00502362  or          eax,0F0h
00502367  mov         byte ptr [byRes],al
}
}

if(pText)
0050236A  cmp         dword ptr [pText],0
0050236E  je          $LN1+0Ah (50248Ah)
{
pText++;
00502374  mov         eax,dword ptr [pText]
00502377  add         eax,1
0050237A  mov         dword ptr [pText],eax

switch(*pText)
0050237D  mov         eax,dword ptr [pText]
00502380  movsx       ecx,byte ptr [eax]
00502383  mov         dword ptr [ebp-0D0h],ecx
00502389  mov         edx,dword ptr [ebp-0D0h]
0050238F  sub         edx,30h
00502392  mov         dword ptr [ebp-0D0h],edx
00502398  cmp         dword ptr [ebp-0D0h],36h
0050239F  ja          $LN21+57h (5023B5h)
005023A1  mov         eax,dword ptr [ebp-0D0h]
005023A7  movzx       ecx,byte ptr  (502554h)[eax]
005023AE  jmp         dword ptr  (502510h)[ecx*4]
{
case 0:
default :
{
pText=0;//конец просмотра строки
005023B5  mov         dword ptr [pText],0
}
break;
005023BC  jmp         $LN1+0Ah (50248Ah)

case '0':byRes|=0x00;break;
005023C1  mov         al,byte ptr [byRes]
005023C4  mov         byte ptr [byRes],al
005023C7  jmp         $LN1+0Ah (50248Ah)
case '1':byRes|=0x01;break;
005023CC  movzx       eax,byte ptr [byRes]
005023D0  or          eax,1
005023D3  mov         byte ptr [byRes],al
005023D6  jmp         $LN1+0Ah (50248Ah)
case '2':byRes|=0x02;break;
005023DB  movzx       eax,byte ptr [byRes]
005023DF  or          eax,2
005023E2  mov         byte ptr [byRes],al
005023E5  jmp         $LN1+0Ah (50248Ah)
case '3':byRes|=0x03;break;
005023EA  movzx       eax,byte ptr [byRes]
005023EE  or          eax,3
005023F1  mov         byte ptr [byRes],al
005023F4  jmp         $LN1+0Ah (50248Ah)
case '4':byRes|=0x04;break;
005023F9  movzx       eax,byte ptr [byRes]
005023FD  or          eax,4
00502400  mov         byte ptr [byRes],al
00502403  jmp         $LN1+0Ah (50248Ah)
case '5':byRes|=0x05;break;
00502408  movzx       eax,byte ptr [byRes]
0050240C  or          eax,5
0050240F  mov         byte ptr [byRes],al
00502412  jmp         $LN1+0Ah (50248Ah)
case '6':byRes|=0x06;break;
00502414  movzx       eax,byte ptr [byRes]
00502418  or          eax,6
0050241B  mov         byte ptr [byRes],al
0050241E  jmp         $LN1+0Ah (50248Ah)
case '7':byRes|=0x07;break;
00502420  movzx       eax,byte ptr [byRes]
00502424  or          eax,7
00502427  mov         byte ptr [byRes],al
0050242A  jmp         $LN1+0Ah (50248Ah)
case '8':byRes|=0x08;break;
0050242C  movzx       eax,byte ptr [byRes]
00502430  or          eax,8
00502433  mov         byte ptr [byRes],al
00502436  jmp         $LN1+0Ah (50248Ah)
case '9':byRes|=0x09;break;
00502438  movzx       eax,byte ptr [byRes]
0050243C  or          eax,9
0050243F  mov         byte ptr [byRes],al
00502442  jmp         $LN1+0Ah (50248Ah)

case 'a':
case 'A':byRes|=0x0a;break;
00502444  movzx       eax,byte ptr [byRes]
00502448  or          eax,0Ah
0050244B  mov         byte ptr [byRes],al
0050244E  jmp         $LN1+0Ah (50248Ah)

case 'b':
case 'B':byRes|=0x0b;break;
00502450  movzx       eax,byte ptr [byRes]
00502454  or          eax,0Bh
00502457  mov         byte ptr [byRes],al
0050245A  jmp         $LN1+0Ah (50248Ah)

case 'c':
case 'C':byRes|=0x0c;break;
0050245C  movzx       eax,byte ptr [byRes]
00502460  or          eax,0Ch
00502463  mov         byte ptr [byRes],al
00502466  jmp         $LN1+0Ah (50248Ah)

case 'd':
case 'D':byRes|=0x0d;break;
00502468  movzx       eax,byte ptr [byRes]
0050246C  or          eax,0Dh
0050246F  mov         byte ptr [byRes],al
00502472  jmp         $LN1+0Ah (50248Ah)

case 'e':
case 'E':byRes|=0x0e;break;
00502474  movzx       eax,byte ptr [byRes]
00502478  or          eax,0Eh
0050247B  mov         byte ptr [byRes],al
0050247E  jmp         $LN1+0Ah (50248Ah)

case 'f':
case 'F':byRes|=0x0f;break;
00502480  movzx       eax,byte ptr [byRes]
00502484  or          eax,0Fh
00502487  mov         byte ptr [byRes],al
}
}

return byRes;
0050248A  mov         al,byte ptr [byRes]
}
0050248D  pop         edi 
0050248E  pop         esi 
0050248F  pop         ebx 
00502490  mov         esp,ebp
00502492  pop         ebp 
00502493  ret             
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #21 : 07-03-2010 14:44 » 

табличный переход там
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #22 : 07-03-2010 14:50 » 

что значит табличный?
Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #23 : 07-03-2010 14:58 » 

это когда вычисляется смещение и делается один переход - без вложенных условий
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #24 : 07-03-2010 15:01 » 

т.е. последовательное сравнение значения *pText с 0, 1, 2, 3, 4.... 9 производится?
« Последнее редактирование: 07-03-2010 15:17 от lapulya » Записан

С уважением Lapulya
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #25 : 07-03-2010 15:02 » 

НЕ производится
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #26 : 07-03-2010 15:14 » 

странно... мда... действительно не производится... ну тогда сорри, свитч рулит...
« Последнее редактирование: 07-03-2010 15:17 от lapulya » Записан

С уважением Lapulya
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines