[VB] Указатели почти как в Си

Хакер дает советы, раскрывает секреты и делится своими мыслями по поводу программирования.

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

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

[VB] Указатели почти как в Си

Сообщение Хакер » 28.02.2011 (Пн) 8:48

В Си чрезвычайно мощная концепция типов. Типы в VB (COM) — намного более ограниченная (в целях безопасности) система.

Тем не менее, я обнаружил (хоть и не первым1) ещё один трюк (см. ниже), который позволит приблизиться по удобству к Си тем, кому необходим простой доступ памяти.

Предположим, что p — указатель (адрес).
Тогда получение (чтение) значения по этому адресу будет выглядеть так: val = *p.
А присвоение (запись) значения по этому адресу так: *p = val.

Унарный оператор * в соответствие указателю ставит то, на что он указывает. А выражение с ним является так называемым l-value.

    На заметку:
    В программирование термином l-value в императивных языках называют выражение, которому можно присвоить значение, то есть выражение, которое может стоять слева (left, отсюда и название) от оператора присваивания (= в VB и Си, := в Паскале). К l-value, как правило можно, применить оператор &, который вернёт адрес l-value.

p может быть не указателем на данные, а указателем на указатель на данные.
Тогда получение (чтение) значения по этому адресу будет выглядеть так: val = **p.
А присвоение (запись) значения по этому адресу так: **p = val.

Антагонистом оператора * является оператор &: он возвращает не то, что по указанному адресу, а адрес того, что указали. Эти два оператора «взаимоуничтожают» эффект друг-друга: взаимоотношения между ними такие же, как между синусом и арксинусом, квадратным корнем и возведением в квадрат.

Есть ли в VB что-то подобное?
  • Получить указатель/адрес легко: аналогом сишного & можно назвать фиктивную2 функцию VarPtr.
  • Получить (прочитать) или присвоить (записать) значение по указателю/адресу на порядок сложнее. Ничего похожего на оператор * мы не имеем. Давно открыты недокументированные функции GetMemN (чтобы прочитать) и PutMemN, позволяющие читать и писать по имеющемуся адресу. Но эти функции по элегантности использования не могут сравниться с оператором *, хотя бы тем, что это две разные функции, а оператор * — один!
    Для примера код на Си с двухуровневыми указателями:
    Код: Выделить всё
    **to = *(*(from)); // Скобки поставлены для тех, кто плохо знаком с указателями в Си.

    Вот как это же действие выполняется на VB с применением GetMem/PutMem:
    Код: Выделить всё
    Dim tmp As Long
    Dim tmp2 As Long
    GetMem4 from, tmp ' Соответствует подвыражению «*(from)»
    GetMem4 tmp, tmp ' Соответствует подвыражению «*(*(from))»
    GetMem4 to, tmp2 ' Соответствует подвыражению «*to»
    PutMem4 tmp2, tmp ' Соответствует выражению «**to = **from»

    Куча строк вместо одного выражения, плюс необходимость в дополнительный переменных. :( Очень некрасиво. :|

Хитрость
Занимаясь исследованием экспортируемых функций vba6.dll, я обратил внимание, что очень многие функции существуют парами (EbGetЧтоНибудь и EbSetЧтоНибудь) и семантика вызова характерна COM: актуальным возвращаемым значением является HRESULT (код статуса), а формальное возвращаемое значение передаётся через последний параметр-ссылку. Кроме того, в сумме семантика вызова для Get- и Set-функций очень уж походила на семантику вызова Get- и Let-хендлеров свойств (Property).

В моём коде EbGetXxxx/EbSetXxxxx использовались вперемешку с GetMemX/PutMemX, и тут меня осенило:
GetMemX и PutMemX — это не просто недокументированные функции, это Get-хендлер и Let-хендлер некоего свойства!

Я напоминаю, что VB (COM) позволяет вам объявлять свойства не только в классах, но и в обычных модулях! И этот как раз тот самый случай: модульные свойства со своими обработчиками. А ещё свойства могут принимать параметр (любого типа).

А ещё у свойств есть очень интересное, особенно в контексте данного разговора, качество — свойство является lvalue, потому что ему можно присвоить значения.

В ту же минутe была написана TLB-шка с объявлением модульного Public-свойства «At», реализациями обработчиков присвоения и получения которого были назначены функции PutMemX и GetMemX из MSVBVM.

Что нам это даёт?
А то, что мы имеем синтаксически эквивалентный аналог сишным оператором * и &.

Несколько аналогий:
Код: Выделить всё
Си:                        VB:

var = *addr;               var = At(addr)     
         
*addr = var;               At(addr) = var

*pNum = 123;               At(pNum) = 123

*pDest = *pSrc + 17;       At(pDest) = At(pSrc) + 17

*(*(*(a))) = ***b;         At(At(At(a))) = At(At(At(b)))     

*(&(var)) экв var          At(VarPtr(var)) экв var

foo(*px, *py)              foo(At(px), At(py))


Для тех, кто мало или вообще не знаком с Си и слабо понимает вышенаписанное, простые примеры:
  • Читаем (и выводим) значение по адресу 400: MsgBox At(400).
  • Присваиваем (записываем) число 123 по адресу 16: At(16) = 123.
  • Читаем значение по адресу 4, умножаем его на семь, записываем по адресу 8: At(8) = At(4) * 7.
  • Присвоение вида a = b с выкрутасами: At(VarPtr(a)) = At(VarPtr(b)).

Особенности:
В Си операндом оператора * должно быть выражение указательного типа (степень индирекции больше нуля). Оператор * сохраняет базовый тип, уменьшая на единицу степень индирекции типа выражения. У нас же псевдооперандом псевдооператора At является Long-значение, а типом возврата будет вполне конкретный тип. Из этого вытекаеют следующие особенности:
  • Объявляено три свойства: At, AtW, AtB — для типов Long, Integer и Byte. Названия даны по аналогии с AscW и AscB.
  • *(x+1) — это всегда значение элемента, отстающего от *x на один элемент.
    At(x + 1) — всегда значение элемента, отстающего от At(x) на один байт.
  • VarPtr в частности, и автоматическое приведение типов вообще, имеющее место быть при передачи чего-либо By Ref, имеет отличие от оператора & в Си. Поэтому корректное в Си выражение &(*(foo)), эквивалентное просто выражению foo, в нашем случае не будет эквивалентным: VarPtr(At(address)) не вернёт address.

Как?
Очень просто, изготавливаете новую или вставляете в существующую библиотеку типов модуль со следующим кодом:
Код: Выделить всё
   [dllname("msvbvm60.dll")]
   module Memory
   {
      [entry("GetMem4"), propget] HRESULT _stdcall At( [in]long  Address, [out, retval]long* rv);
      [entry("GetMem4"), propget] HRESULT _stdcall At_([in]void* Address, [out, retval]long* rv);
      [entry("PutMem4"), propput] HRESULT _stdcall At( [in]long  Address, [in]long nv);
      [entry("PutMem4"), propput] HRESULT _stdcall At_([in]void* Address, [in]long nv);

      [entry("GetMem2"), propget] HRESULT _stdcall AtW([in]long   Address, [out,retval]short* rv);
      [entry("PutMem2"), propput] HRESULT _stdcall AtW([in]long   Address, [in]short nv);
      [entry("GetMem2"), propget] HRESULT _stdcall AtW_([in]void* Address, [out,retval]short* rv);
      [entry("PutMem2"), propput] HRESULT _stdcall AtW_([in]void* Address, [in]short nv);

      [entry("GetMem1"), propget] HRESULT _stdcall AtB([in]long   Address, [out, retval]unsigned char* rv);
      [entry("PutMem1"), propput] HRESULT _stdcall AtB([in]long   Address, [in]unsigned char nv);
      [entry("GetMem1"), propget] HRESULT _stdcall AtB_([in]void* Address, [out, retval]unsigned char* rv);
      [entry("PutMem1"), propput] HRESULT _stdcall AtB_([in]void* Address, [in]unsigned char nv);
   }


Здесь я добавил ещё вариант с символом подчёркивания — у него обязательный параметр свойства будет иметь тип As Any, что позволит в качестве адреса указывать всё, что угодно, минуя VarPtr.

Те же, кого смущает само название «At», могут переименовать его в «MemAt» или «AtAddress» или «MemAtAddress» или как вам ещё будет угодно.

Возможно я сделаю свою TLB и выложу её в «кирпичный завод» в недалёком будущем.

Сноски:
  1. На момент написания этой статьи обнаружилась существующей TLB-шка, датированная 2002 годом за авторством некоего Michel-а Rutten-а.
  2. Фиктивную, потому что вся «магия» заключается не коде этой функции, а в её прототипе (объявлении) и автоматическом неявном кастовании, производимом VB.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

BV
Thinker
Thinker
Аватара пользователя
 
Сообщения: 3987
Зарегистрирован: 12.09.2004 (Вс) 0:55
Откуда: Молдавия, г. Кишинёв

Re: [VB] Указатели почти как в Си

Сообщение BV » 28.02.2011 (Пн) 13:45

У тебя AtB возвращает long, пофиксть код в tlb
const char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;

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

Re: [VB] Указатели почти как в Си

Сообщение Хакер » 28.02.2011 (Пн) 13:51

Пофиксил.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: [VB] Указатели почти как в Си

Сообщение arthur2 » 28.02.2011 (Пн) 14:17

Здорово :)
Тлб-шку себе уже скомпилировал :) Пойду поэксперементирую
Артур
 
   

BV
Thinker
Thinker
Аватара пользователя
 
Сообщения: 3987
Зарегистрирован: 12.09.2004 (Вс) 0:55
Откуда: Молдавия, г. Кишинёв

Re: [VB] Указатели почти как в Си

Сообщение BV » 28.02.2011 (Пн) 16:50

Хакер писал(а):Пофиксил.

Get пофиксил, Put не пофиксил
const char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;

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

Re: [VB] Указатели почти как в Си

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

Теперь всё пофиксил.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Debugger
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1667
Зарегистрирован: 17.06.2006 (Сб) 15:11

Re: [VB] Указатели почти как в Си

Сообщение Debugger » 28.02.2011 (Пн) 22:15

Круто!
Планирую это использовать в своих проектах, это очень удобно.
И (судя по тестам) вызов по указателю по скорости такой же, как и просто вызов функции.

SLIM
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1840
Зарегистрирован: 04.04.2008 (Пт) 18:21
Откуда: Краснодар

Re: [VB] Указатели почти как в Си

Сообщение SLIM » 28.02.2011 (Пн) 22:54

Клево!
Были бы это еще реальные указатели... :)
Пишите жизнь на чистовик.....переписать не удастся.....

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

Re: [VB] Указатели почти как в Си

Сообщение Хакер » 01.03.2011 (Вт) 10:13

Debugger писал(а):И (судя по тестам) вызов по указателю по скорости такой же, как и просто вызов функции.

Ты про этот мой проект?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Debugger
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1667
Зарегистрирован: 17.06.2006 (Сб) 15:11

Re: [VB] Указатели почти как в Си

Сообщение Debugger » 01.03.2011 (Вт) 15:34

Да.

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

Re: [VB] Указатели почти как в Си

Сообщение Хакер » 05.07.2011 (Вт) 0:10

Кстати, если есть желание выпендриться, можно объявить GetMem4 не как «AtL», а как «int*» (именно так, со звёздочкой).

А потом писать так:
Код: Выделить всё
val = [int*](address)


Хотя с точки зрения кошения под Си, синтаксически верно было бы её объявлять как «*(int*)», но так выглядит некрасиво:
Код: Выделить всё
v = [*(int*)](address)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: [VB] Указатели почти как в Си

Сообщение ger_kar » 05.07.2011 (Вт) 18:39

Хакер писал(а):Очень просто, изготавливаете новую или вставляете в существующую библиотеку типов модуль со следующим кодом:

Все конечно очень клево, но блин я нифига не знаю как делать TLB, тем более что там судя по коду Си, которого я в упор не понимаю. Да и учить его что то мне не очень охота (хотя наверное стоило бы). Поэтому вопрос, а можно это все использовать без TLB оформив все это дело в отдельный класс с использованием Let и Get Конечно это уже будет не так красиво (прийдется писать через точку), но будет ли вообще это работать также?
Бороться и искать, найти и перепрятать

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

Re: [VB] Указатели почти как в Си

Сообщение Хакер » 05.07.2011 (Вт) 20:25

ger_kar писал(а):но блин я нифига не знаю как делать TLB

На этот случай я написал статью.

ger_kar писал(а):тем более что там судя по коду Си

Буээ... там MIDL.

ger_kar писал(а):Поэтому вопрос, а можно это все использовать без TLB оформив все это дело в отдельный класс с использованием Let и Get Конечно это уже будет не так красиво (прийдется писать через точку), но будет ли вообще это работать также?

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

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: [VB] Указатели почти как в Си

Сообщение ger_kar » 05.07.2011 (Вт) 20:43

Хакер писал(а):Буээ... там MIDL.
Вот я внатуре лажанулся :oops: :) Для меня все что непонтно, все Си :) Час от часу не легче еще и MIDL, ситуация для меня стала еще более запутанной. Это сколько всего знато-то нужно ё-моё :shock:
А статью я бы с удовольствем прочитал бы.
Бороться и искать, найти и перепрятать

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: [VB] Указатели почти как в Си

Сообщение ger_kar » 29.11.2011 (Вт) 13:35

Хакер писал(а):Возможно я сделаю свою TLB и выложу её в «кирпичный завод» в недалёком будущем.
Кстати, а почему бы тебе ее и не выложить? Наверняка у тебя она уже имеется и пригодна к употреблению. Я как не пытался родить TLB из предложенного кода, так нифига и не родил. И после нескольких абсолютно бесплодных попыток так и бросил эту гиблую (для себя) затею.
Бороться и искать, найти и перепрятать


Вернуться в МануAll

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

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

    TopList