Странный пример кода из документации

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

Re: Странный пример кода из документации

Сообщение iskander » 02.12.2019 11:24:57

serbod писал(а):Меня смутил "var" при объявлении поля f.

Обыкновенное объявление начала секции переменных, в данном случае полей.
iskander
энтузиаст
 
Сообщения: 606
Зарегистрирован: 08.01.2012 18:43:34

Re: Странный пример кода из документации

Сообщение Dmitro25 » 02.12.2019 11:47:00

serbod
Вот здесь можно про этот "var" прочитать касательно Delphi.
Dmitro25
незнакомец
 
Сообщения: 7
Зарегистрирован: 29.11.2019 20:02:46

Re: Странный пример кода из документации

Сообщение Cheb » 02.12.2019 23:39:28

P:=@TTest.Myproc; и P:=@TTest.TestProc3; скомпилируются и выполнятся, но вот при попытке вызова P произойдёт крах.
Угадай, почему.
Спойлер: -> скрытый параметр Self <-
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Странный пример кода из документации

Сообщение java73 » 03.12.2019 11:44:44

Уже писали, что
TTest = object ?
Попробуйте как class объявить.
java73
постоялец
 
Сообщения: 257
Зарегистрирован: 21.11.2013 09:08:10

Re: Странный пример кода из документации

Сообщение Dmitro25 » 04.12.2019 11:05:45

java73 писал(а):Уже писали, что
TTest = object ?
Попробуйте как class объявить.

Пробовал. Ругается на ключевое слова "static"; если его заменить на "class procedure" - точно так же даёт скомпилировать данный пример в режиме {$mode delphi}
Dmitro25
незнакомец
 
Сообщения: 7
Зарегистрирован: 29.11.2019 20:02:46

Re: Странный пример кода из документации

Сообщение Alex2013 » 04.12.2019 14:21:03

Возможно больше смысла имела бы обратная операция присвоение методу класса указателя на процедуру ... ( Я вот сейчас мучаюсь примерно такой же дурью в OpenVR.dll (написанном на с++) засунули класс, а мне нужно добраться до него из Лазаруса... Причем кто-то уже разобрался и сделал модуль OpenVR.pas но инструкцию по применению не приложил вот сижу и уже месяц ломаю башку пытаясь понять что чему соответствует.... )

Зы
Сделано примерно так
Код: Выделить всё
Type
     PVR_IVRCompositor_FnTable=^TVR_IVRCompositor_FnTable;
     TVR_IVRCompositor_FnTable=record
      SetTrackingSpace:procedure(eOrigin:TETrackingUniverseOrigin);
        {$ifdef Windows}stdcall;{$else}cdecl;{$endif}
      GetTrackingSpace:function:TETrackingUniverseOrigin;
      {$ifdef Windows}stdcall;{$else}cdecl;{$endif}
     ...
    end;

Честно говоря совершенно не понимаю как умудрились использовать названия методов из длл напрямую .
Но в конце есть чуть более понятный кусок ...
Код: Выделить всё
procedure LoadOpenVR(const aLibName:PChar);
begin
FreeOpenVR;
OpenVRLibraryHandle:=LoadLibrary(aLibName);
if OpenVRLibraryHandle=0 then begin
  raise Exception.Create(Format('Could not load library: %s',[aLibName]));
end else begin
  @VR_InitInternal:=GetProcAddress(OpenVRLibraryHandle,'VR_InitInternal');
  @VR_ShutdownInternal:=GetProcAddress(OpenVRLibraryHandle,'VR_ShutdownInternal');
  @VR_IsHmdPresent:=GetProcAddress(OpenVRLibraryHandle,'VR_IsHmdPresent');
  @VR_GetGenericInterface:=GetProcAddress(OpenVRLibraryHandle,'VR_GetGenericInterface');
  @VR_IsRuntimeInstalled:=GetProcAddress(OpenVRLibraryHandle,'VR_IsRuntimeInstalled');
  @VR_GetVRInitErrorAsSymbol:=GetProcAddress(OpenVRLibraryHandle,'VR_GetVRInitErrorAsSymbol');
  @VR_GetVRInitErrorAsEnglishDescription:=GetProcAddress(OpenVRLibraryHandle,'VR_GetVRInitErrorAsEnglishDescription');
end;
end;
Последний раз редактировалось Alex2013 04.12.2019 15:00:16, всего редактировалось 3 раз(а).
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Странный пример кода из документации

Сообщение Dmitro25 » 04.12.2019 14:39:02

Похоже, разобрался с помощью статьи GunSmoker'a и Help-a от Embarcadero
В родном компиляторе Delphi в обычный метод класса передаётся скрытый параметр Self - указатель на данные экземпляра данного класса;
в метод, помеченный ключевым словом "class" (например "class procedure") скрытый параметр также передаётся, но он указывает не на данные экземляра, а на данные, относящиеся к классу в целом (это позволяет делать такие методы виртуальными и перекрывать их в классах-потомках), и компилятор следит, чтобы внутри такого метода не было кода, который бы обращался переменным или методам данного класса, которые не являются классовыми (т.е. не обозначены ключевым словом "class");
в метод, который помечен и "class", и "static" (например, "class procedure testproc2; static;"), скрытый параметр вообще не передаётся (как следствие, такие методы нельзя делать виртуальными и перекрывать в классах-потомках).

В результате метод, который помечен как "class procedure", совместим по сигнатуре с "procedure of object", а метод "class procedure testproc2; static;" совместим по сигнатуре просто с "procedure".
Таким образом пример из моего первого сообщения в случае использования родного компилятора Delphi можно переписать следующим образом:
Код: Выделить всё
{$APPTYPE CONSOLE}
{$IFDEF FPC}{$MODE DELPHI}{$H+}{$ENDIF}
type
  TTest = object
  const
    Epsylon = 100;
  var
    f: integer;
    class var cv1, cv2: integer;
    procedure myproc;
    class procedure testproc;
    class procedure testproc2; static;
    class procedure testproc3; static;
  end;

  procedure TTest.myproc;
  begin
    cv1 := 0;
    f := 1;
  end;

  class procedure TTest.Testproc;
  begin
    cv1 := 1;
    // f:=1;
  end;

  class procedure TTest.Testproc2;
  begin
    cv1 := 2;
    // f:=1;
  end;

  class procedure TTest.Testproc3;
  begin
    cv1 := 3;
    // f:=1;
  end;

var
  P: procedure;
begin
  P := @TTest.Myproc;
  P := @TTest.Testproc;
  P := TTest.Testproc2;
  P := TTest.Testproc3;
end.

Изменено определение метода "testproc3", т.к. компилятор Delphi не даёт помечать как "static" неклассовые методы, и, самое главное, в основной программе можно корректно присвоить "P := TTest.Testproc2;" и "P := TTest.Testproc3;", без приведения к бестиповому указателю. Первые два присваивания по-прежнему не компилируются из-за несоответствия их сигнатур переменной "P".

FPC в режиме {$mode delphi} также компилирует приведённый выше пример. Удалось выяснить, что FPC несколько по-разному относится к применению ключевых слов "static" и "class" к объектам (object) и классам (class). В случае объектов ("TTest = object") если объявить в примере выше:
Код: Выделить всё
    class procedure testproc;
    class procedure testproc2; static;
    procedure testproc3; static;       

то, с одной стороны, код процедуры "testproc3" не будет иметь возможности доступа к локальным полям объекта, а с другой - компилятор будет выдавать ошибку при попытке присвоить "P := TTest.Testproc3;", т.е., похоже, что компилятор рассматривает объявление "procedure testproc3; static;" как "class procedure testproc3;" (без "static").
В случае классов ("TTest = class") компилятор просто отказывается компилировать строку "procedure testproc3; static;", т.е. требует, чтобы это было либо "class procedure testproc3;", либо "class procedure testproc3; static". Такое поведение является, на мой взгляд, более правильным, похоже, при компиляции кода с объектами имеется какая-то недоработка самого компилятора.
Ещё хочу отметить, что при использовании FPC, если сделать попытку присвоения процедурной переменной адреса метода, который не совпадает с ней по сигнатуре (например, написать "P := TTest.Testproc;" в коде выше, без знака "@"), то вместо выдачи внятного сообщения об ошибке, компилятор генерирует ошибку "Error: Internal error 200301042".
Dmitro25
незнакомец
 
Сообщения: 7
Зарегистрирован: 29.11.2019 20:02:46

Пред.

Вернуться в Обучение Free Pascal

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

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

Рейтинг@Mail.ru