Доступ к подфункциям / процедурам из DPR или другой функции / процедуры в Delphi - PullRequest
3 голосов
/ 19 мая 2010

Насколько я знаю - подпрограммы с режимом частного доступа к родительской функции / процедуре, верно?

Есть ли способ получить к ним доступ из "внешнего мира" - dpr или другая функция / процедура в модуле?

Кроме того - каким способом требуется больше вычислений и места для скомпилированного файла?

например:

function blablabla(parameter : tparameter) : abcde;
 procedure xyz(par_ : tpar_);
 begin
  // ...
 end;
begin
 // ...
end;

procedure albalbalb(param : tparam) : www;
begin
 xyz(par_ : tpar_); // is there any way to make this function public / published to access it therefore enabling to call it this way?
end;

// all text is random.

// also, is there way to call it from DPR in this manner?

// in C++ this can be done by specifing access mode and/or using "Friend" class .. but in DELPHI?

Ответы [ 4 ]

7 голосов
/ 19 мая 2010

Вложенные процедуры / функции - те, которые объявлены внутри другой процедуры или функции, являются особым типом, потому что они могут обращаться к стеку (и, следовательно, к параметрам / локальным переменным) процедуры, в которую они вложены. Из-за этого и области видимости Delphi правила, нет доступа к ним вне "родительской" процедуры. Вы используете их, только если вам нужно воспользоваться их специальными функциями. AFAIK Delphi / Pascal - один из немногих языков, обладающих этой функцией. С точки зрения компилятора вызов имеет некоторый дополнительный код, позволяющий получить доступ к фрейму родительского стека, IIRC. AFAIK Класс / функции «друга» в C ++ отличаются - они являются методами доступа к классам, в то время как в вашем примере вы используете простые процедуры / функции. В Delphi все процедуры / классы, объявленные в одном модуле, автоматически становятся «друзьями», если только строгие частные объявления не используются в последних выпусках Delphi. Например, этот фрагмент кода будет работать, пока все находится в одном блоке:

  type
    TExample = class
    private
      procedure HelloWorld;
    public
    ...
    end;

  implementation

    function DoSomething(AExample: TExample);
    begin
      // Calling a private method here works
      AExample.HelloWordl;
    end;
5 голосов
/ 19 мая 2010

Примечание: встроенные подпрограммы <> частные / защищенные методы.

Встроенные подпрограммы, т.е. подпрограммы внутри подпрограмм, не могут быть доступны внешним подпрограммам. Вы опубликовали пример встроенной подпрограммы, я также слышал, что она называется Внутренние подпрограммы.

Вот еще один пример:

procedure DoThis;

function DoThat : Boolean;
begin
  // This Routine is embedded or internal routine.
end;
begin

// DoThat() can only be accessed from here no other place.

end;

Независимо от наглядности методы классов можно вызывать с помощью Delphi 2010 через RTTI. Я подробно рассказал, как это сделать в этой статье .

Если вы находитесь в одном модуле, методы класса могут быть доступны для любого другого кода независимо от видимости, если только они не помечены как Strict Private. Этот вопрос содержит более подробную информацию и хороший пример кода в принятом ответе .

Если вы находитесь в двух разных подразделениях, вы можете использовать взлом защищенных методов для доступа к защищенным методам. Что подробно описано в этой статье .

3 голосов
/ 06 сентября 2012

Да, вы можете получить доступ к подпрограмме, которая вложена в другую (родительскую) подпрограмму из внешнего мира. Хотя это несколько сложно. Я нашел это руководство в Интернете.

Как передать вложенную подпрограмму в качестве процедурного параметра (32 бита)

Delphi обычно не поддерживает передачу вложенных подпрограмм в качестве процедурных параметров:

// This code does not compile:
procedure testpass(p: tprocedure);
begin
  p;
end;
procedure calltestpass;
 procedure inner;
 begin
   showmessage('hello');
 end;
begin
  testpass(inner);
end;

Очевидный обходной путь - передать адрес процедуры и типизировать его в пределах теста:

// This code compiles and runs OK
procedure testpass(p: pointer);
begin
  tProcedure(p);
end;
procedure calltestpass;
 procedure inner;
 begin
   showmessage('hello');
 end;
begin
  testpass(@inner);
end;

Однако в приведенном выше примере есть ловушка - если «внутренняя» процедура ссылается на любую переменную, которая была помещена в стек до того, как «внутренняя» процедура была вызвана из testpass (параметры calltestpass - если они были, или локальные переменные в calltestpass - если они были), ваша система наиболее вероятно вылетает:

// This code compiles OK but generates runtime exception (could even be
//  EMachineHangs :-) )
procedure testpass(p: pointer);
begin
  tProcedure(p);
end;
procedure calltestpass;
var msg: string;
 procedure inner;
 begin
   msg := 'hello';
   showmessage(msg);
 end;
begin
  testpass(@inner);
end;

Причина заключается в том, что простыми словами был "сломан" при вызове процедуры testpass и "внутренней" процедуры неправильно рассчитывает параметры и расположение локальных переменных (не вините Дельфи, пожалуйста). Обходной путь должен установить правильный контекст стека перед «внутренний» вызывается изнутри «тестпасс».

// This code compiles and runs OK
{$O-}
procedure testpass(p: pointer);
var callersBP: longint;
begin
  asm // get caller's base pointer value at the very beginning
    push dword ptr [ebp]
    pop callersBP
  end;
// here we can have some other OP code
  asm // pushes caller's base pointer value onto stack and calls tProcedure(p)
    push CallersBP
    Call p
    Pop  CallersBP
  end;
// here we can have some other OP code
end;
{$O+}

procedure calltestpass;
var msg: string;
 procedure inner;
 begin
   msg := 'hello';
   showmessage(msg);
 end;
begin
  testpass(@inner);
end;

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

2 голосов
/ 19 мая 2010

Нет, нет способа сделать то, что вы просите. Функция xyz может вызываться только с помощью функции blablabla. За пределами этой функции xyz не находится в области видимости, и его нельзя назвать. Если бы C ++ разрешал вложенную функцию, то и ссылки на нее не было бы, точно так же, как нет способа ссылаться на функции со статическим связыванием извне текущей единицы перевода.

Если вам нужно вызвать xyz извне функции blablabla, то переместите xyz снаружи. Если вам нужно вызвать его извне текущего модуля, вам необходимо объявить эту функцию в разделе интерфейса устройства. Затем добавьте этот модуль в предложение uses внешнего кода, и вы сможете вызывать xyz из любого места, даже из файла DPR.

Если xyz относится к переменным или параметрам функции blablabla, вам нужно будет передать их как параметры, так как в противном случае xyz больше не будет иметь к ним доступ.

Концепция спецификаторов доступа здесь не очень актуальна, поскольку мы не говорим о классах. У модулей есть интерфейс и реализация разделов, которые на самом деле не совпадают с public и private разделами класса.

...