Radinator
Radinator

Reputation: 1088

Dynamic call of internal procedure

May someone explain me why I can't assign the address of a internal procedure, which name is stored in a string variable, to a ProcPointer with the %paddr() BIF?

My idea was to create a associative array with numers as key and the name of a certain subprocedure as value. When the user enters the number 2 in the DSPF then the program has to call the procedure that has the key '2'.

As I understood the BIF %paddr() takes either the hard coded procedure name or a string which contains the name of the procedure. But when giving a string variable the compiler complains that the parameter for %PADDR is invalid.

Upvotes: 1

Views: 1276

Answers (2)

Mihael
Mihael

Reputation: 255

The parameter name for %paddr must be constant, as Charles and Mark already said.

The easiest way is to create an array of procedure pointers.

Here a little example:

**FREE

ctl-opt main(main) dftactgrp(*no) actgrp(*caller);


dcl-c UPPER 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
dcl-c LOWER 'abcdefghijklmnopqrstuvwxyz';


dcl-proc main;
  dcl-pi *n;
    index char(1) const;
  end-pi;

  dcl-s line char(50);
  dcl-s procedureName char(256);
  dcl-s transformPtr pointer(*proc);

  dcl-pr transform char(50) extproc(transformPtr);
    input char(50) const;
  end-pr;

  dcl-s procedures pointer(*proc) dim(2);
  procedures(1) = %paddr('TOLOWER');
  procedures(2) = %paddr('TOUPPER');

  transformPtr = procedures(%int(index));

  line = 'Hello, world. =)';
  line = transform(line);
  dsply line;
end-proc;


dcl-proc toUpper;
  dcl-pi *n char(50);
    input char(50) const;
  end-pi;

  return %xlate(UPPER : LOWER : input);
end-proc;


dcl-proc toLower;
  dcl-pi *n char(50);
    input char(50) const;
  end-pi;

  return %xlate(LOWER : UPPER : input);
end-proc;

Upvotes: 2

jmarkmurphy
jmarkmurphy

Reputation: 11473

This is because procedures are bound statically by the compiler. So the compiler needs to know at compile time what procedure to call. However, there are some API's that can be used to bind service programs at run time. Alan Campin has some examples here.

Let's talk a little more about binding. IBM i provides two different types of binding when calling programs and procedures. For programs the binding occurs at run time the first time a given program is called, it is resolved and bound to the caller. Program names can be held in variables, and because the program is bound at run time, the first time the program is called via a variable, it is resolved and bound to the caller, after that, when the value in the variable is changed, the program is resolved again at the next call. This is called dynamic binding. The binding occurs at run time, potentially every time a program is called, and the bindings are lost when the caller or the activation group ends.

Procedures are bound statically during compile time. There is no built in run time binding for procedures or procedure pointers. You can approximate dynamic binding by using procedure pointers, but internally the procedure is bound to the procedure pointer, and if you provide an address for the procedure pointer via %paddr(), the binding occurs at that point. Even API's that use callbacks are bound statically. The API is bound to the procedure pointer, and the caller binds the procedure pointer to the callback procedure itself, then passes the binding to the API. No resolution happens at run time. You can test that with this simple program.

**free
ctl-opt dftactgrp(*no) actgrp(*new) BndDir('mybnddir');

dcl-s procPtr       Pointer(*proc);

dcl-pr proc         ExtProc(procPtr);
end-pr;

procPtr = %paddr('MissingProcedure');
proc();
return;

The program does not compile. It fails in the binding step because it can't find the MissingProcedure. The API expecting to receive a callback as a parameter, however is able to compile even without knowing what the callback is or if it exists because it only binds to the procedure pointer itself which is already internal to the program.

So RPG does not provide any facilities to bind a procedure at run time (dynamically), but IBM i does provide system API's that we can use to manually resolve a procedure in a service program. This is what Alan Campin's examples that I mentioned above do. So technically yes we can dynamically bind to procedures in a service program, but RPG does not provide the facilities to do so. That only happens at compile time.

Upvotes: 6

Related Questions