Страница 1 из 2

Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 01.02.2015 04:08:21
mgear
Нигде в интернетах ответа не смог найти. Или меня одного это интересует, или задачка не решается в рамках FPC. При этом в дельфи пашет как от нефиг делать.

Есть callback функции, объявленные через процедурный тип, чтобы использоваться во внешней библиотеке. Их там две штуки, но для простоты приведу одну:

Код: Выделить всё
type
  pcre2_malloc_func = function(size:PCRE2_SIZE; p:pointer):pointer; cdecl;
  function pcre2_general_context_create_16(malloc_f: pcre2_malloc_func; free_f: pcre2_free_proc; memory_data:pointer): p_pcre2_general_context; cdecl; external;

function pcre2_malloc_def(size:PCRE2_SIZE; p:pointer):pointer; cdecl;
begin
  Result := getmem(size);
end;

...
fgeneral_context := pcre2_general_context_create_16(@pcre2_malloc_def, @pcre2_free_def, nil);


Ну и как бы зашибись всё работает. Но это неаккуратненько, доктор. Потому что вспомогательная функция класса объявлена вне класса.

Мы-то на объектном языке пишем, и вызов используется в классе, так что идеологически верно было бы оформить в качестве метода и описать, естественно, как class static, чтоб вызывалась без селфов разных там.

Код: Выделить всё
    class function malloc_def(size:PCRE2_SIZE; p:pointer):pointer; cdecl; static;


Но в этом случае компилятор ругается:
Error: Incompatible type for arg no. 1: Got "<class method type of function(LongWord;Pointer):^untyped of object;CDecl>", expected "<procedure variable type of function(LongWord;Pointer):^untyped;CDecl>"


Причём в хелпе русским английским языком сказано следующее:
FPC knows static class methods in classes: these are class methods that have the Static keyword at the end. These methods behave completely like regular procedures or functions. This means that:

They do not have a Self parameter. As a result, they cannot access properties or fields or regular methods.
They cannot be virtual.
They can be assigned to regular procedural variables.


Выделено мною. Тем не менее, assign to regular procedural variable не фурычит.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 01.02.2015 15:00:15
Дож
Мне не удалось воспроизвести ошибку.
Код: Выделить всё
[doj@larion ~/temp]$ cat staticproc.pas
{$MODE DELPHI}
type
TFunc = function(Size: Integer; P: Pointer): Pointer; cdecl;
TMyObject = class
  class function malloc_def(Size: Integer; P: Pointer): Pointer; cdecl; static;
end;

class function TMyObject.malloc_def(Size: Integer; P: Pointer): Pointer;
begin
  Result := P;
end;

var
  F: TFunc;

begin
  F := TMyObject.malloc_def;
end.
[doj@larion ~/temp]$ fpc staticproc.pas
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
[doj@larion ~/temp]$

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 01.02.2015 20:01:14
kazalex
Дож писал(а):Мне не удалось воспроизвести ошибку.

{$MODE DELPHI} убери :)

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 02.02.2015 01:10:47
mgear
Я уж по-всякому корячился, но фантазия иссякла, а воз и ныне там. Даже принудительно тип указателя присвоить не получилось. Компилятор упирается. Не буду, - говорит, - компилировать и всё тут. Несоответствие, - говорит, - типов.

Моде дельфи не хочу. Не по-пацански это. Хочу уметь объявлять тип метода. А то прям как в той песенке получается: жопа есть, а слова нет.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 02.02.2015 01:24:23
kazalex
mgear писал(а):Даже принудительно тип указателя присвоить не получилось.

Хм, странно. У меня с приведением сработало.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 02.02.2015 03:43:04
mgear
Ну ок, вот тестовый юнит. Он компилируется. Задача - раскомментировать строчку и скомпилять в режиме objfpc хоть через какое угодно переписывание методов и приведение типов.

Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

type
  pcre2_malloc_func = function(size:longint; p:pointer):pointer; cdecl;
  pcre2_free_proc = procedure(p1:pointer; p2:pointer); cdecl;

  pcre2_general_context = record end;
  p_pcre2_general_context = ^pcre2_general_context;

  myclass = class
  private
     fgeneral_context: p_pcre2_general_context;
     class function malloc(size:longint; p:pointer):pointer; cdecl; static;
     class procedure free(p1:pointer; p2:pointer); cdecl; static;
  public
     procedure aaa;
  end;

function pcre2_general_context_create_16(malloc_f: pcre2_malloc_func; free_f: pcre2_free_proc; memory_data:pointer): p_pcre2_general_context; cdecl;

implementation

function _malloc(size:longint; p:pointer):pointer; cdecl;
begin
  Result := nil;
end;

procedure _free(p1:pointer; p2:pointer); cdecl;
begin
end;

function pcre2_general_context_create_16(malloc_f: pcre2_malloc_func;
  free_f: pcre2_free_proc; memory_data: pointer): p_pcre2_general_context;
  cdecl;
begin
  Result := nil;
end;

class function myclass.malloc(size: longint; p: pointer): pointer; cdecl;
begin
  Result := nil;
end;

class procedure myclass.free(p1: pointer; p2: pointer); cdecl;
begin

end;

procedure myclass.aaa;
begin
  fgeneral_context := pcre2_general_context_create_16(@_malloc, @_free, nil);
//  fgeneral_context := pcre2_general_context_create_16(@malloc, @free, nil);
end;

end.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 02.02.2015 10:24:11
kazalex
mgear писал(а):Задача - раскомментировать строчку и скомпилять в режиме objfpc хоть через какое угодно переписывание методов и приведение типов.

Код: Выделить всё
  fgeneral_context := pcre2_general_context_create_16(pcre2_malloc_func(@myclass.malloc), pcre2_free_proc(@myclass.free), nil);

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 02.02.2015 18:18:35
mgear
kazalex, спасибо!!!

Почему-то паскаль ругается "Error: Illegal type conversion" на pcre2_malloc_func(@malloc), при этом спокойно кушает pcre2_malloc_func(@myclass.malloc). Хотя вызов осуществляется из метода класса, так что уточнение "myclass.", вроде как, избыточное. Вот и поди догадайся. Хорошо хотя бы он не просит "unit1.myclass." :lol: :lol: :lol:

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 04.02.2015 16:18:49
wavebvg
mgear писал(а):Почему-то паскаль ругается "Error: Illegal type conversion" на pcre2_malloc_func(@malloc), при этом спокойно кушает pcre2_malloc_func(@myclass.malloc). Хотя вызов осуществляется из метода класса, так что уточнение "myclass.", вроде как, избыточное. Вот и поди догадайся. Хорошо хотя бы он не просит "unit1.myclass." :lol: :lol: :lol:

Потому что Вы через ссылку на таблицу в Self пытается вызвать, что недопустимо для компилятора.
Просто это не предусмотрено в самом компиляторе, потому что class метод может быть виртуальным и тогда... Ну в общем тогда нужно все предусмотреть, так что проще запретить стрелять себе в голову, предварительно запретив стрелять в себя с открытыми глазами. Разименовав указатель и правильно пересчитав смещение вы можете вызвать всё, что угодно. Только оно Вам надо?

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 04.02.2015 18:33:34
mgear
wavebvg писал(а):Только оно Вам надо?

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

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

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 04.02.2015 18:59:22
kazalex
mgear писал(а): По сути, это является диверсией и надругательством над психикой. Тем этом в официальном хелпе написано, что они есть. Как так?

Обычный баг, коих появляется вагон, как только начинаешь писать более-менее сложный код.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 05.02.2015 00:10:44
mgear
kazalex, больше похоже на то, что сделано специально.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 05.02.2015 00:29:46
kazalex
mgear, по сообщению об ошибке видно же, что баг:
project1.lpr(53,85) Error: Incompatible type for arg no. 2: Got "<class method type of procedure(Pointer;Pointer) of object;CDecl>", expected "<procedure variable type of procedure(Pointer;Pointer);CDecl>"

Добавлено спустя 37 минут 21 секунду:
Вот еще прикол. В режиме {$mode delphi} прокатывает даже если у методов класса (malloc и free) полностью изменить сигнатуры :mrgreen:

Код: Выделить всё
class procedure myclass.free();
begin
end;

procedure myclass.aaa;
begin
  fgeneral_context := pcre2_general_context_create_16(@myclass.malloc, @myclass.free, nil);
end;

Проверено на транковом компиляторе.

Добавлено спустя 14 минут 51 секунду:
Но если убрать оператор взятия адреса, то прокатывать перестает :)

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 05.02.2015 09:18:59
alexs
А написать метод-обёртку над вызовом обращением к процедурной переменной? Заодно в нём можно проверки необходимые на корректность вызова добавить.
А саму процедурную переменную хранить в защищённой секции класса.

Re: Как объявить процедурный тип для class ... static?

СообщениеДобавлено: 05.02.2015 10:35:08
mgear
alexs, именно так сейчас и сделано. Судя по всему, так и останется.