0

I'm working on a project where I'm trying to compile a VxWorks END in C++. I don't think I'll be able to change working in C++, and I definitely can't change VxWorks too much.

This has gone mostly fine, save for one issue. VxWorks requires all drivers to register their methods with in a net_funcs struct, which has the following fields (among others):

#ifdef __cplusplus
extern "C" {
#endif
...

typedef struct net_funcs
    {
    ...
    STATUS (*pollSend) (END_OBJ*, M_BLK_ID); /* Driver's polling send func. */
    STATUS (*pollRcv) (END_OBJ*, M_BLK_ID); /* Driver's polling recv func. */

    /*
     * The minimum required arguments for (*formAddress)() are:
     * (*formAddress)(M_BLK_ID, M_BLK_ID, M_BLK_ID, BOOL)
     */
    M_BLK_ID (*formAddress) ();           /* Driver's addr formation func. */

    /*
     * The minimum required arguments for (*packetDataGet)() are:
     * (*packetDataGet)(M_BLK_ID, LL_HDR_INFO *)
     */
    STATUS (*packetDataGet) ();           /* Driver's addr formation func. */
    ...
    } NET_FUNCS
...
#ifdef __cplusplus
}
#endif

Note the C-style empty parameter list. In C, this means that this field could be a functor that accepts any argument; however, when compiling in C++, it views these arguments as (void), then throws an error when I attempt to instantiate a struct. If I try to change the arguments to ( first arg, ...), passing in the default lib file calls (endEtherAddressForm and endEtherPacketDataGet, both called from lib file as recommended by the software manual) causes an error because those functors require four specific arguments and don't accept variable numbers of args.

I might be able to just hardcode it to the argument list of the default functions and hope for the best, but before I do, is there any way to make this code work with a variable argument list in both C and C++? Or is there something I have to do to make an extern "C" struct work across two files?

UPDATE:

When instantiating the struct, I use the following code:

LOCAL NET_FUNCS MBbufferNetFuncs =
    {
    ...
    MyCode::EndPollSend,
    MyCode::EndPollRcv,
    endEtherAddressForm, /* Library function from endLib.lib; declaration
        M_BLK_ID endEtherAddressForm(M_BLK_ID, M_BLK_ID, M_BLK_ID, BOOL)*/
    endEtherPacketDataGet /* Library function from endLib.lib; declaration
        STATUS endEtherAddressForm(M_BLK_ID, LL_HDR_INFO *)*/
    }

The error states that:

"A value of type "M_BLK_ID (*)(M_BLK_ID, M_BLK_ID, M_BLK_ID, BOOL)" cannot be used to initialize an entry of type "M_BLK_ID (*)()""

Normally, one would think that the extern "C" declaration at the start of the net_func declaration would prevent empty parameter list problems, but this is not the case. I do not know if I need to add special code when I declare my personal copy of the struct, but it does not appear to work.

Garrett
  • 115
  • 10
  • 3
    `extern "C" {` `}` ? – KamilCuk Jun 25 '18 at 22:10
  • ...That is a beautiful thing that I did not know existed. Thank you so much! – Garrett Jun 25 '18 at 22:12
  • Update: However, it doesn't seem to be working. The entire end.h file was already wrapped in a #ifdef __cplusplus extern "C" #endif statement. Is there something I need to do to have a passed argument registered as an extern "C"? – Garrett Jun 25 '18 at 22:20
  • How about some more detail than "it doesn't seem to be working."? – chux - Reinstate Monica Jun 25 '18 at 22:28
  • I've added the declaration where I get the error, and the error message itself. If you know where else I can look for bad data, I'd be happy to provide it. I can't copy code directly out of my internet-less VM, so it's been harder than usual to post code blocks. – Garrett Jun 25 '18 at 22:45
  • structure accepts `M_BLK_ID (*formAddress) ();`. You state you are trying to initialize it with `M_BLK_ID endEtherAddressForm(M_BLK_ID, M_BLK_ID, M_BLK_ID, BOOL)`. They don't have the same signature, so of course they do not fit. You have a round hole. Find a round peg. If the library documentation say to do this, something has gone very wrong in the documentation or it's out of date. – user4581301 Jun 25 '18 at 23:17
  • @user4581301 Please read the post title. VxWorks uses a C-style empty parameter list M_BLK_ID (*formAddress) (). See top answer here: https://stackoverflow.com/questions/13950642/why-does-a-function-with-no-parameters-compared-to-the-actual-function-definiti This is an obsolescent feature of C; however, it is used in a library function in VxWorks, and I must go with it. The use of these functions are explicitly encouraged; c.f. page 84, Wind River Network Stack for VxWorks 6 Programmer's Guide, 6.6 vol 3. – Garrett Jun 25 '18 at 23:59
  • 2
    `extern "C"` doesn't change the language being used or the meaning of any syntax. The only things it does are: 1. require the compiler to do the correct thing if a declaration is either defined using C or used by C source (which may involve things like skipping name mangling and/or using a different function calling convention), 2. cause declarations in different namespaces to name the same thing, and 3. change the type of functions and function pointers so that functions with different language linkage aren't accidentally used interchangeably. – aschepler Jun 26 '18 at 00:29
  • 3
    You could write *just* the registration function in a `.c` file – o11c Jun 26 '18 at 00:30
  • 2
    My apologies I missed the paragraph that explained that part of the problem. I think you're trapped into a using a cast here, because C++ just doesn't allow mismatched signatures. You know that the C code using the structure can handle it, so it should be "safe" to lie to the compiler: `(M_BLK_ID (*)())endEtherAddressForm`. – user4581301 Jun 26 '18 at 00:32
  • @user4581301 That... actually solves the problem quite nicely. The syntax for redefining one of the VxWorks macros was a bit messy in the end, but that nicely solved the problem, and manages to fit with the intent of the c-style declaration. Thanks for the help! – Garrett Jun 26 '18 at 11:03

2 Answers2

1

I'm a bit rusty with C and C++, but if memory serves...

The C syntax for function arguments is:

void foo1();              // Zero or more unspecified args
void foo2(int a, ...);    // One or more args
void foo3(void);          // No args

The C++ syntax is:

void bar1(...);           // Zero or more unspecified args
void bar2(int a, ...);    // One or more args
void bar3();              // No args
David R Tribble
  • 11,918
  • 5
  • 42
  • 52
0

I can’t test with your setup, but I found (clang -Wpedantic) was happy with:

extern "C" int f(...);
int r(int c) {
    return f(c) * c;
}
int main(void) { 
    return r(2);
}

It even worked, so must not be undefined behaviour... :) However you need to do something a bit uglier like:

ifdef __cplusplus
#define CLARGS ...
#else
#define CLARGS
#endif

then make your declarations:

M_BLK_ID (*formAddress) (CLARGS);

To prevent C++ from leaking on your C code.

mevets
  • 10,070
  • 1
  • 21
  • 33
  • It's definitely something about VxWorks's compiler, then. For whatever reason, it's throwing fits when I try to use (...) as the argument list for the macros. But, if user4581301's solution of using recasting to emulate C-style empty argument lists doesn't work out, using CLARGS to add cplusplus only requirements definitely seems like the way to go. Thanks! – Garrett Jun 26 '18 at 11:06