6

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?

l4mpi
  • 5,103
  • 3
  • 34
  • 54
  • Don't see what this has to do with C. Your problem is name mangling of the C++ wrappers, no? – Jens Gustedt Aug 13 '12 at 12:11
  • 2
    This could be an excellent argument to upgrade to C++11, which doesn't have this restriction. – ecatmur Aug 13 '12 at 12:13
  • 1
    @JensGustedt, correct, but I also tagged it C because the library is written in C and technically it's a C/C++ interoperability problem. – l4mpi Aug 13 '12 at 12:19
  • @ecatmur C++11 does not support anonymous structs: http://stackoverflow.com/questions/8622459/why-does-c11-not-support-anonymous-structs-while-c11-does – Morwenn Aug 13 '12 at 12:23
  • Note that your fix breaks type safety on the Handle and Request types since it causes them to both have the same type. You'd need to create each type separately `typedef struct OpaqueHandle { unsigned long x*; } *Handle;` – Adam Bowen Aug 13 '12 at 12:27
  • 1
    Said differently I don't think that any expertise on C can help you here. So this calls for the wrong people (me for example :) – Jens Gustedt Aug 13 '12 at 12:30
  • @Morwenn that's about anonymous structs within another aggregate. – ecatmur Aug 13 '12 at 13:31

4 Answers4

3

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • this works, but would still require a change in the library. Also, can you explain your answer a bit - why use two MAKE_DUMMY defines instead of one? – l4mpi Aug 13 '12 at 13:54
  • 1
    @l4mpi the two defines are necessary to get `__LINE__` expanded. The preprocessor is *weird*; see http://stackoverflow.com/questions/1597007/creating-c-macro-with-and-line-token-concatenation-with-positioning-macr – ecatmur Aug 13 '12 at 13:56
  • We have asked the library vendor if they can include this fix. If they decline we will employ it locally... until someone has the time and courage to test if we can safely upgrade our compiler and C++ version from gcc4.1/C++98 to gcc4.7/C++11. – l4mpi Aug 14 '12 at 13:11
1

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);
    ...
};
}
justin
  • 104,054
  • 14
  • 179
  • 226
  • 2
    The ctors and conversion operator can't be declared, because their signature would contain an anonymous type. – ecatmur Aug 13 '12 at 12:17
  • @ecatmur thanks for noting that (+1). my suggestion for ctors and conversion ops was not compatible with the compiler/settings the OP's using -- removed. – justin Aug 13 '12 at 12:32
  • This would have probably been the right way to do it, but I'm hesitant to implement this as it would require changes in a big part of the codebase. – l4mpi Aug 13 '12 at 13:49
1

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Why does declaring `make` work in this case? It uses the anonymous struct in its signature... shouldn't that lead to the same name mangling problems? – l4mpi Aug 13 '12 at 14:13
  • 1
    @l4mpi In this case it's OK because `make` is static and so doesn't have external linkage. – ecatmur Aug 13 '12 at 14:17
0

This seems to work:

class MyCls {
  public:
    typedef _Opaque MHandle;
    static void* myFct(MHandle handle) {
      return 0;
    }   
};
perreal
  • 94,503
  • 21
  • 155
  • 181