Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with C library anonymous struct types in C++

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?

like image 317
l4mpi Avatar asked Aug 13 '12 11:08

l4mpi


People also ask

What is an anonymous struct in C?

An anonymous struct declaration is a declaration that declares neither a tag for the struct, nor an object or typedef name. Anonymous structs are not allowed in C++. The -features=extensions option allows the use of an anonymous struct declaration, but only as member of a union.

How is anonymous structured?

Anonymous unions/structures are also known as unnamed unions/structures as they don't have names. Since there is no names, direct objects(or variables) of them are not created and we use them in nested structure or unions. Definition is just like that of a normal union just without a name or tag.

Can C struct have private members?

Structure with private members in C++ Sometimes a question arose "Can a structure has private members?" The answer is "Yes! In C++, we can declare private members in the structure". So the important thing is that "we can declare a structure just like a class with private and public members".

What is the difference between union and anonymous union?

An anonymous union is a union without a name. It cannot be followed by a declarator. An anonymous union is not a type; it defines an unnamed object. The member names of an anonymous union must be distinct from other names within the scope in which the union is declared.


2 Answers

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.

like image 179
ecatmur Avatar answered Oct 21 '22 01:10

ecatmur


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);
    ...
};
}
like image 1
justin Avatar answered Oct 21 '22 02:10

justin