Using SYS$ routines with C++

Post Reply

Topic author
garyrevell
Active Contributor
Posts: 38
Joined: Thu Nov 19, 2020 7:15 am
Reputation: 0
Location: Basingstoke, UK
Status: Offline
Contact:

Using SYS$ routines with C++

Post by garyrevell » Thu Nov 19, 2020 7:55 am

Hi all,

I want to write some C++ code that calls system services and have used CXX to compile and CXXLINK to link the image. This works fine if there are no calls to SYS$ etc, if there are then I get undefined symbols from the linker.

So, my question is.... what do I need to add to the CXXLINK command to get the SYS$ routines included? Do I need a .OPT file, and if so, then what goes in it?

Thanks!

Gary


marty.stu
Site Admin
Valued Contributor
Posts: 96
Joined: Tue May 21, 2019 6:56 am
Reputation: 0
Status: Offline

Re: Using SYS$ routines with C++

Post by marty.stu » Thu Nov 19, 2020 8:27 am

Hi Gary,

Please provide the exact error that you are getting from the linker. Also, if that's not too much to ask, could you show the exact piece of code that the linker complains about?

Thank you,

Maxim Megalinskiy
VSI Training Team
Run to the bedroom, In the suitcase on the left You'll find my favorite axe.


Topic author
garyrevell
Active Contributor
Posts: 38
Joined: Thu Nov 19, 2020 7:15 am
Reputation: 0
Location: Basingstoke, UK
Status: Offline
Contact:

Re: Using SYS$ routines with C++

Post by garyrevell » Thu Nov 19, 2020 8:59 am

marty.stu wrote:
Thu Nov 19, 2020 8:27 am
Hi Gary,

Please provide the exact error that you are getting from the linker. Also, if that's not too much to ask, could you show the exact piece of code that the linker complains about?

Thank you,

Maxim Megalinskiy
VSI Training Team
Hi Maxim,

Here's an example of a simple use of SYS$SETPRN....

Code: Select all

/*  This program shows a call to system service SYS$SETPRN.    */
#include <ssdef.h>
#include <stdio.h>
#include <string.h>

#include <iostream>
#include <descrip.h>

#define PROCNAMELEN 15


int SYS$SETPRN( struct dsc$descriptor_s* );


int main(int argc, char** argv)
{
    int ret;
    struct  dsc$descriptor_s  name_desc;
    const char  *name =  "Gary R";         /* Define default process name */
    char nameBuff[PROCNAMELEN+1];

    strcpy( nameBuff , name );
    name_desc.dsc$w_length = strlen(name);      /* Length of name WITHOUT null terminator */
    name_desc.dsc$a_pointer =  nameBuff;        /* Put address of string buffer in descriptor */
    name_desc.dsc$b_class =  DSC$K_CLASS_S;     /* String descriptor class */
    name_desc.dsc$b_dtype =  DSC$K_DTYPE_T;     /* Data type: ASCII string */

    cout << "Setting process name to " << name << endl;

        ret = SYS$SETPRN(&name_desc);
        switch ( ret )
        {
            case SS$_NORMAL:
            {
                printf("Process name set to %s\n", nameBuff );
                break;
            }
            case SS$_DUPLNAM:
            {
                fprintf(stderr, "Duplicate name detected %s\n", nameBuff );
                break;
            }
            default:
            {
                fprintf(stderr, "Failed to set process name to %s - unexpected error %d!!\n", nameBuff,ret );
                break;
            }
        }

    return ret;
}

Compile & link it:

$ CXX setp
$ CXXLINK setp
%ILINK-W-NUDFSYMS, 1 undefined symbol:
%ILINK-I-UDFSYM, int SYS$SETPRN(dsc$descriptor_s *)
%ILINK-W-USEUNDEF, undefined symbol int SYS$SETPRN(dsc$descriptor_s *) referenced
source code name: "SYS$SETPRN(dsc$descriptor_s*)"
section: .text
offset: %X0000000000000140 slot: 2
module: SETP
file: MYROOT:[dev.cxx]setp.OBJ;2

OK, so, I declared SYS$SETPRN as taking a pointer to a dsc$descriptor_s struct ($DESCRIPTOR) as I guessed that's what it needed.

Can you tell me how I should declare the SYS$SETPRN routine, and how to link it in via CXXLINK?

I can get it to work no problem when coding it as a C program.

Thanks!

Gary


sms
Master
Posts: 310
Joined: Fri Aug 21, 2020 5:18 pm
Reputation: 0
Status: Online

Re: Using SYS$ routines with C++

Post by sms » Thu Nov 19, 2020 9:23 am

Code: Select all

> Can you tell me how I should declare the SYS$SETPRN routine, and how to
> link it in via CXXLINK?

its $ cxx setp2
its $ cxxlink setp2
its $ run setp2
Setting process name to Gary R
Process name set to Gary R

its $ gdiff setp.cxx setp2.cxx
12c12,13
< int SYS$SETPRN( struct dsc$descriptor_s* );
---
> /* int SYS$SETPRN( struct dsc$descriptor_s* ); */
> #include <starlet.h>

ITS $ cxx /version
HP C++ V7.4-005 on OpenVMS IA64 V8.4    


   Note that <starlet.h> also does things like:

      #define  sys$setprn  SYS$SETPRN

which allows you to avoid the unsightly/tasteless upper-case "SYS$"
names.
Last edited by sms on Thu Nov 19, 2020 9:33 am, edited 1 time in total.


Topic author
garyrevell
Active Contributor
Posts: 38
Joined: Thu Nov 19, 2020 7:15 am
Reputation: 0
Location: Basingstoke, UK
Status: Offline
Contact:

Re: Using SYS$ routines with C++

Post by garyrevell » Thu Nov 19, 2020 9:47 am

Great, works fine.

So the thing I was missing was the #include <starlet.h>

Simple when you know how? :)


sms
Master
Posts: 310
Joined: Fri Aug 21, 2020 5:18 pm
Reputation: 0
Status: Online

Re: Using SYS$ routines with C++

Post by sms » Thu Nov 19, 2020 10:11 am

Code: Select all

> So the thing I was missing was the #include <starlet.h>

   More providing your own (mismatched) prototype, I'd say.  I do
approximately nothing with C++, so I know nothing, but I gather that C++
takes those things seriously.  (Note the arg type in the %ILINK
complaints.  It's not complaining about plain-old "SYS$SETPRN".)  If you
extract <starlet.h> from the text library
("SYS$LIBRARY:SYS$STARLET_C.TLB"?), you'll see things like:

#ifdef __NEW_STARLET
int sys$setprn(
        void *prcnam);
#else   /* __OLD_STARLET */
int sys$setprn(__unknown_params);
#endif  /* #ifdef __NEW_STARLET */

Neither of which has an argument list which looks like yours.

   Again, I know nothing, but I'd guess that that "void *" is related to
its accepting all/many descriptor types, mentioning all of which would
be worse.


mw
Member
Posts: 8
Joined: Mon Jul 06, 2020 4:38 am
Reputation: 0
Status: Offline

Re: Using SYS$ routines with C++

Post by mw » Sat Nov 21, 2020 9:20 am

You are using CXXLINK, which is just a wrapper around the VMS linker. On IA64 this is no longer necessary. The only "advantage" is that you do not see the C++ mangled symbols in any linker message. However, directly using the VMS linker may make your problem more obvious:

Code: Select all

$ link setprn
%ILINK-W-NUDFSYMS, 1 undefined symbol:
%ILINK-I-UDFSYM,        CX3$Z10SYS$STPRNP16DSC$D04URF6I 
%ILINK-W-USEUNDEF, undefined symbol CX3$Z10SYS$STPRNP16DSC$D04URF6I referenced
        source code name: "SYS$SETPRN(dsc$descriptor_s*)"
        section: .text
        offset: %X00000000000001B0  slot: 2
        module: SETPRN 
        file: DISK$WORK:[MW]SETPRN.OBJ;1 
$
The C++ compiler mangled your extern declaration of SYS$SETPRN. But in SYS$PUBLIC_VECTORS (which by default is included in a link and that's the place where all the system services are defined) there is only the symbol SYS$SETPRN. So there is no match with you mangled symbol.

Including starlet.h is probably the right thing, but sometimes the prototypes are not very helpful. At least a void* will not cause a compiler diagnostic if you pass a non-descriptor address.

What you will find in starlet.h is an extern "C" wrapper around all the system service declarations. You can do that in your source as well:

Code: Select all

--- SETPRN.CXX;-1       2020-11-21 08:45:25 -0500
+++ SETPRN.CXX  2020-11-21 09:02:58 -0500
@@ -9,7 +9,7 @@
 #define PROCNAMELEN 15
 
 
-int SYS$SETPRN( struct dsc$descriptor_s* );
+extern "C" int SYS$SETPRN( struct dsc$descriptor_s* );
 
 
 int main(int argc, char** argv)
Then you have the expected check of the parameters and the linker can resolve the symbol.

Post Reply