2016-01-19 5 views
1

Я пробовал очень долго, чтобы найти ответ для этого, но ничего не нашел. Я пытаюсь передать обратный вызов с данными в функцию.Обратный вызов с параметрами и результатом

Это мой псевдокод:

TGetFreeNumber = function(data: Pointer): integer; 


procedure TMyClass.AddToDatabase(cb: TGetFreeNumber{???}); 
var 
    num: integer; 
begin 
    num := {{Invoke cb with parameters}}; 
    // INSERT INTO ... VALUES num ... 
end; 


procedure TMyClass.impl1(from, till: integer); 

    function myCB(data: Pointer); 
    begin 
    result := 123; 
    end; 

begin 
    AddToDatabase(@myCB(someData){???}); 
end; 


procedure TMyClass.impl2(from, till: integer); 

    function myCB(data: Pointer); 
    begin 
    result := 456; 
    end; 

begin 
    AddToDatabase(@myCB(someData){???}); 
end; 

Я сделал некоторые эксперименты с TMethod, но я не найти подходящий метод Invoke как

procedure TMyClass.AddToDatabase(cb: TMethod); 
var 
    num: integer; 
begin 
    num := Invoke(cb.Code, cb.Data); 
    // INSERT INTO ... VALUES num ... 
end; 
+1

Передача вложенные функций не является хорошей идеей. –

+0

Да, локальные функции несовместимы с процедурными типами, такими как 'TGetFreeNumber'. – kludg

ответ

4

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

procedure TMyClass.AddToDatabase(cb: TGetFreeNumber; data: Pointer); 
var 
    num: integer; 
begin 
    num := cb(data); 
    // INSERT INTO ... VALUES num ... 
end; 

И вы также не должны использовать вложенную функцию для вашего обратного вызова. Это противоречит языковым правилам для ссылки на вложенную функцию. documentation говорит:

Вложенные процедуры и функции (процедуры, объявленные в других подпрограммах) не могут использоваться в качестве процедурных значений.

Таким образом, вы должны написать этот код следующим образом:

function myCB(data: Pointer); 
begin 
    result := 456; 
end; 

procedure TMyClass.impl2(from, till: integer); 
begin 
    AddToDatabase(myCB, someData); 
end; 

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

Из-за исторической причуды 32-битный компилятор Windows позволит использовать вложенную функцию в качестве процедурной переменной, если вы используете оператор @ для подавления проверки типов. Кроме того, вызовы этой процедурной переменной будут работать до тех пор, пока вложенная функция не будет ссылаться ни на что в ее внешней области, или что-либо содержащееся в Self. На мой взгляд, на это поведение нельзя полагаться.

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

type 
    TGetFreeNumber = function(data: Pointer): integer of object; 

и передать метод вашего класса.

Или, если вы предпочитаете использовать анонимные методы и переменный захват, и ваша версию Delphi поддерживает их, а затем использовать опорную функцию:

type 
    TGetFreeNumber = reference to function(data: Pointer): integer; 
+0

Спасибо за подсказки. У меня есть следующий код, который работает http://pastebin.com/EmcK6n7G. Но я хотел бы решить проблемы, о которых вы говорили. 1) Мой Delphi 2007 не знает «ссылки на». 2) Когда я удаляю «@», я получаю ошибку 'E2094: Локальная процедура/функция '..', назначенная переменной процедуры', потому что это внутренняя процедура. 3) Почему я не должен использовать вложенные процедуры? Проблема в том, что этот обратный вызов требуется только для этой единственной реализации.Я старался не обращаться к внешним переменным, так как это приводит к проблемам. –

+0

Я думаю, что все эти вопросы объясняются в ответе. Подавление допустимой ошибки при использовании '@' на самом деле не так, как я буду заниматься программированием. Другие скажут вам, что нормально использовать вложенные функции, но я не согласен. Так же делают правила языка. –

1

Он должен быть простым:

TGetFreeNumber = function(data: Pointer): integer; 


procedure TMyClass.AddToDatabase(cb: TGetFreeNumber; Data: Pointer); 
var 
    num: integer; 
begin 
    num := cb(Data); 
    // INSERT INTO ... VALUES num ... 
end; 
Смежные вопросы