Dealing with C library anonymous struct types in C++

1.8k views Asked by At

We have a big, old C++ application with a lot of legacy code and a few external libraries written in C. These libraries are very rarely updated - only if we find a bug and the vendor supplies a patch. This happened last week with one library, and upon integrating the new version we found out that if we don't modify the library locally (which we apparently did with the last version) our build breaks with this error message:

non-local function ‘static E* MyCls::myFct(<anonymous struct>*)’ uses anonymous type

This is due to the library declaring a number of handle types like this:

#define _Opaque struct {unsigned long x;} *

typedef _Opaque    Handle;
typedef _Opaque    Request;

which we use on our side in some classes' function signatures:

class MyCls {
public:
    static void* myFct(Handle handle);
    ...
}

This produces the error above because the compiler can't create a proper name-mangled name for the function(s) as the _Opaque struct has no name.

Our current workaround for this is to patch the library header file, explicitly giving the struct a name:

//#define _Opaque struct {unsigned long x;} * //Replaced by typedef below!
typedef struct __Opaque {unsigned long x;} * _Opaque;

This is obviously bad because we don't want to touch the library if possible. Another even worse option would be to convert the types to void* in all function signatures and cast them back to their respective types. And there's the worst option to rewrite every affected function in pure C...

So, my question is: Is there any better option than patching the library? Is there an easy solution I am overlooking? What would be the best way to solve this?

4

There are 4 answers

3
ecatmur On BEST ANSWER

You can accomplish this with a minimal change to the #define line, exploiting the rule in 7.1.3:8 that the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only:

#define MAKE_DUMMY2(line) dummy_ ## line
#define MAKE_DUMMY(line) MAKE_DUMMY2(line)
#define _Opaque struct {unsigned long x;} MAKE_DUMMY(__LINE__), *

This gives Handle and Request etc. minimal linkage.

3
justin On

You can introduce names by declaring new types, which simply contain these elements. Use those types for your parameters.

namespace MON {
struct t_handle {
  Handle handle;
};

class MyCls {
public:
    static void* myFct(t_handle handle);
    ...
};
}
0
perreal On

This seems to work:

class MyCls {
  public:
    typedef _Opaque MHandle;
    static void* myFct(MHandle handle) {
      return 0;
    }   
};
2
ecatmur On

If you're willing to modify your methods on the interface, you can do slightly better than void *:

struct CHandle {
    void *p;
    CHandle(void *p): p(p) { }
};
struct CRequest {
    void *p;
    CRequest(void *p): p(p) { }
};

static CHandle make(Handle handle) { return CHandle(handle); }
static Handle get(CHandle handle) { return static_cast<Handle>(handle.p); }
static CRequest make(Request request) { return CRequest(request); }
static Request get(CRequest request) { return static_cast<Request>(request.p); }

Here, CHandle and CRequest have linkage and so can be used in your method signatures; the overloads of make and get have internal linkage and so can interface with the anonymous types. You can put this in a header, even the static functions.

You'll have to modify your code so that when e.g. MyCls::myFct calls into the library, you wrap parameters with get and return values with make.