2

I need to work in a project that involves interaction between a Progress 9.1D application and C language functions. I'm in charge of writing the C function, and the Progress people have asked me to investigate if it is possible to send a Progress table and receive it in the C function as a struct.

I have searched on the Internet for days and in some Progress manuals (Progress_External_Programming_Interfaces) but I have found very little information about this. As I understand the table may be sent to the C function as a MEMPTR variable but I'm not sure...

Please, can someone point me to some tutorial or an example of this?

Thanks a lot.

user1274605
  • 405
  • 2
  • 6
  • 17

4 Answers4

1

Depending on your platform, it's possible to make calls to external libraries, and there's documentation on how to do that (HLC calls I think it is).

While it's not possible to send a whole table to a library, it would be possible to:

 FOR EACH table-name NO-LOCK:
    /* transfer table data to memptr */
    /* make C call */
    /* interpret results */
 END.

If you were working in a sufficiently recent version of 10.*, it could be possible to take a TT, convert that to an XML structure in a MEMPTR, and then send that to the C code.

For the V11.0 docs, it's in the "OE Development: Programming Interfaces" doc under "Host Level Call Interface"

Tim Kuehn
  • 3,201
  • 1
  • 17
  • 23
1

9.1D is, of course, ancient, obsolete and unsupported but even so it does support calling C routines as DLLs. For some detailed examples you might find this presentation on UNIX Shared Libraries helpful. (The gory details vary but the same ideas work for Windows if that's your environment.)

Simple example of the 4GL side:

define variable x as integer no-undo.
define variable c as memptr  no-undo.
define variable m as memptr  no-undo.

procedure sprintf external "/lib64/libc.so.6":
  define input-output parameter fStr as memptr.
  define input  parameter mask as memptr.
  define input  parameter arg  as double.
  define return parameter x    as long.
end.

set-size( c ) = 1024.
set-size( m ) = 1024.
put-string( m, 1 ) = "%1.4e".
run sprintf( input-output c, m, 0.0123, output x ).

display get-string( c, 1 ) format “x(20)”.

return.

The main issue for you, as the C programmer, is providing an API and data structure that the 4GL guys will be able to work with easily.

The simplest method is to just do plain old simple parameters (like above). Possibly one record at a time (depending on what your function is doing that may, or may not make sense -- if you need a set of records to work with then you would need to collect them and manage them somehow).

If it is really necessary to create structs and build them with memptrs you're going to have to work closely with the 4GL guys to define an API and agree on what the memptr should look like. I think that is likely to be a lot of work -- I'd try to avoid it and find a way to go with simple parameters if I could.

Tom Bascom
  • 13,405
  • 2
  • 27
  • 33
0

This can be done fairly easily: either as Tim Kuehn or Tom Bascom suggest or:

Allocate sufficiently large memory region to hold the data:

def var mp as memptr no-undo.
set-size(mp) = 1024. /* pre-calculated size depending on the data, in bytes */
/* copy contents of temp-table into mp */
run procedureInC (mp). /* run external procedure */
set-size(mp) = 0.    /* free up the memory */

There are three major things that need to be filled in:

  1. Deciding how data should be structured within the memory region, this includes agreement on how 4GL data types will be represented (i.e. representation and length of strings, decimals, dates etc) and good understanding how your particular C compiler lays out structs in memory.
  2. Once there is an agreement on the first point 4GL guys can roll out a function that given a temp-table handle will calculate the required size of the memory region based on record size calculated using point 1 and number of records.
  3. Next 4GL guys can roll out a procedure copying temp-table into the memory region following the rules agreed in point 1 (look at PUT-BYTE, PUT-STRING etc in Progress Manual).

There is one more thing to remember, since the memory region is going to be allocated by Progress runtime and then freed straight after the call to your C function, you'd need to copy the content as it won't be any longer available once you return.

  1. Tim's solution is the simplest to implement from 4GL point of view, but you'd need to parse XML in C and it will use significantly more memory than the other two solutions.
  2. Tom's solution is a compromise. The data is sent in binary form, however it requires a bit more effort and skill on 4GL side. You'd need to roll out an API, manage state and the data is going to be transferred in a number of successive calls.
  3. The solution I described is what you have asked for. It allows to pass the data with minimum overhead all in one go. However, it's going to be configuration dependent - the layout of structs generated by your C compiler matters.

The other problem is that third solution requires someone on 4GL side who understand how data is represented on the machine level so you could have a meaningful conversation about various string representations and encodings, endianness, padding, representation of Progress decimals in C without loss of precision, representation of dates and so on. Clearly since 4GL people asked you to investigate the solution they might not be conversant in all these topics and hence you're better off with simplifying the task for them as much as possible and doing most of the work for them.

Another way is to hire a suitable consultant on 4GL side to do and thoroughly document this piece of work for you.

Yet another way is to keep asking smaller questions here on Stackoverflow whilst doing the bulk of the investigation and programming work yourself - we'll be happy to help. Good luck.

Community
  • 1
  • 1
Vlad Gudim
  • 23,397
  • 16
  • 69
  • 92
0

If the data you are passing isn't huge, 9.1D has a pretty decent basic DOM object that I'd use on the Progress side to construct my data in XML. Then I'd pass it to the C function. Not sure what your OS is, but in Windows you can easily pass this to a COM object or a DLL using EXTERNAL within the 4GL (limitation because of 9.1D, 11.0 would allow you to do this via .NET w/o GUI). We've done the same sort of thing using Linux and shared libraries. The XML method removes the requirement to change you objects. You could also look into the ProxyGen tool if you are using the AppServer.

Don.

Don
  • 1