C++ vs. Я

Модератор: Хакер

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

C++ vs. Я

Сообщение Хакер » 15.09.2008 (Пн) 16:08

Говорят, что шаблоны — чрезвычайно гибкая штука...

Есть такая вещь, как неймспейсы. Их ввели для того, чтобы обойти проблему коллизий имён. Но кто мешает мне юзать неймспейсы как контейнеры для некоторых прединициализированных глобальных объектов?

Мой глобальный объект это как минимум три сопряженных сущности: некая последовательность байтов (являющаяся исполняемым кодом), константа определяющая длину этого кода, и константа, определяющая смещение некого места внутри кода относительно начала этого кода.

Лучшего способа хранить такие объекты я не нашёл (если кто-то вдруг предложит — буду рад). Глобальный прединициализированный массив структур не подходит например потому, что:
1) К объектам, хранящимся в нём нужно обращаться по индексам, что крайне неудобно. К неймспейсам можно обращаться по их именам.
2) У большинства объектов по три свойства, но у некоторых их до 6. В структуре кол-во полей должно быть фиксированным, а в неймсейсе кол-во членов может быть любым.
3) Я не знаю способа (его вероятно не существует) инициализировать элемент массива кодом, который получится из скомпилированного сишного кода. В неймспейсе это делается легко — в него просто помещается функция.

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

Создав макрос xxx(y), раскрывающийся в y::Code, y::Size, y::Offset можно очень легко и красиво работать с такими глобальными прединициализированными объектами.

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





Но понадобилось мне сделать такой шаблонный объект. И тут нас встречает первая неприятность: namespace в С++ не может быть шаблонным. Т.е. перспектива юзать макрос xxx(y<foo>) уже теряется.

Можно, например, вместо неймспейса использовать класс — он может быть шаблонным, но в нём не может быть naked-функций. Так что здесь облом.

Ладно, думаю я: сделаю шаблонной саму функцию, и напишу на этот случай отдельный макрос.

Делаем:
Код: Выделить всё
   namespace NcdPrefixFlagSetter
   {
      template <long PrefixFlag> void __declspec(naked) Code()
      {
         __asm
         {
            bts ebx,PrefixFlag;
            mov eax, 0x12345678;
            jmp eax;
         }
      }             

      unsigned long const Size = 11UL;
      unsigned long const MagicSpot = 5UL;
   }



Получаем облом следующего вида:
Ошибка писал(а):error C2420: 'PrefixFlag' : illegal symbol in second operand


Т.е. шаблонный переменный внутри __asm-блоков каким-то боком не обрабатываются. Какого, спрашивается, фига? :x И что теперь, спрашивается, делать? Объявлять 11 одинаковых неймспейсов, с 11 почти одинаковыми функциями, отличающимися исключительно циферкой в инструкции BTS? :x

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

Чем-нибудь нешаблонным может быть, на первый взгляд, локальная переменная или константа внутри функции, но это только на первый взгляд, ибо попробовав поюзать её мы получим:
Ошибка писал(а):error C2489: 'tmpPrefixFlag' : initialized auto or register variable not allowed at function scope in 'naked' function

Т.е. внутри naked-функции такой трюк не сработает: а перенести нечто нешаблонное за пределы функции нельзя, у нас шаблон распростняется только на функцию.

Остаётся только одно: вызывать из шаблонной функции другую функцию (нешаблонную) и передать ей в качестве параметра значение шаблонной переменной. Вторую функцию сделать naked и обязательно инлайновой — тогда сгенирируется именно такой код, как нам хочется.

Т.е.:

Код: Выделить всё
   namespace NcdPrefixFlagSetter
   {
      void __inline __declspec(naked) CodeThunk(const long x)
      {
         __asm
         {
            bts ebx,x;
            mov eax, 0x12345678;
            jmp eax;
         }
      }

      template <long PrefixFlag> void __declspec(naked) Code()
      {
         CodeThunk(PrefixFlag);
      }      

      unsigned long const Size = 11UL;
      unsigned long const MagicSpot = 5UL;
   }


Но вновь нас ждёт облом. Теперь уже не со стороны шаблонов, а со стороны инлайн-асма. Компилятор ругается на "bts ebx, x" оскорбительной фразой "improper operand type". Дело в том, что в качестве второго операнда BTS может быть либо imm8, либо регистр, а по мнению компилятора, наша x никак не imm8.

Но это обходится крайне легко:
1) вместо bts юзаем and
2) Вместо PrefixFlag юзаем 1 << PrefixFlag (т.е. вместо номера бита производим битовую маску).


В итоге получаем следующее:
Код: Выделить всё
   namespace NcdPrefixFlagSetter
   {
      void __inline __declspec(naked) CodeThunk(const long x)
      {
         __asm
         {
            and ebx, x
            mov eax, 0x12345678;
            jmp eax;
         }
      }

      template <long PrefixFlag> void __declspec(naked) Code()
      {
         CodeThunk(1 << PrefixFlag);
      }      

      unsigned long const Size = 11UL;
      unsigned long const MagicSpot = 5UL;
   }


Этот вариант компилируется на ура.

Но есть новый и более сложный облом: указатель на шаблонную функцию ни к чему не кастуется...
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: C++ vs. Я

Сообщение Хакер » 15.09.2008 (Пн) 16:50

Второй облом удалось решить двойным кастованием.

Но появился новый облом. Компилятор плевать хотел на то, что у нас функция CodeThunk инлайновая.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: C++ vs. Я

Сообщение Хакер » 15.09.2008 (Пн) 17:16

В MSDN говорится, что Naked-функция не может быть инлайновой.

Вот облом...
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: C++ vs. Я

Сообщение Хакер » 15.09.2008 (Пн) 17:47

Короче говоря, нарвавшись на ещё пару обломов, я посылаю шаблоны нафиг.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.


Вернуться в Мой блог

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

    TopList