我们有一个庞大的旧C ++应用程序,其中包含许多遗留代码和一些用C编写的外部库。这些库很少更新 - 只有当我们发现错误并且供应商提供补丁时。上周发生了一个库,在集成新版本时,我们发现如果我们不在本地修改库(我们显然使用上一版本),我们的构建就会出现这个错误消息:
non-local function ‘static E* MyCls::myFct(<anonymous struct>*)’ uses anonymous type
这是因为库声明了许多这样的句柄类型:
#define _Opaque struct {unsigned long x;} *
typedef _Opaque Handle;
typedef _Opaque Request;
我们在一些类的函数签名中使用的:
class MyCls {
public:
static void* myFct(Handle handle);
...
}
这会产生上面的错误,因为编译器无法为函数创建正确的名称错位名称,因为_Opaque结构没有名称。
我们目前的解决方法是修补库头文件,明确地给结构命名:
//#define _Opaque struct {unsigned long x;} * //Replaced by typedef below!
typedef struct __Opaque {unsigned long x;} * _Opaque;
这显然很糟糕,因为如果可能的话我们不想触摸库。另一个更糟糕的选择是将所有函数签名中的类型转换为void*
并将它们转换回各自的类型。并且在纯C中重写每个受影响的函数是最糟糕的选择...
所以,我的问题是:有没有比修补图书馆更好的选择?我有一个简单的解决方案吗?解决这个问题的最佳方法是什么?
答案 0 :(得分:3)
您可以通过对#define
行的最小更改来完成此操作,利用7.1.3中的规则:8 声明声明的第一个typedef-name
类类型(或枚举类型)用于表示类型(或枚举类型)仅用于链接目的:
#define MAKE_DUMMY2(line) dummy_ ## line
#define MAKE_DUMMY(line) MAKE_DUMMY2(line)
#define _Opaque struct {unsigned long x;} MAKE_DUMMY(__LINE__), *
这为Handle
和Request
等提供了最小的联系。
答案 1 :(得分:1)
您可以通过声明仅包含这些元素的新类型来引入名称。将这些类型用于参数。
namespace MON {
struct t_handle {
Handle handle;
};
class MyCls {
public:
static void* myFct(t_handle handle);
...
};
}
答案 2 :(得分:1)
如果您愿意在界面上修改方法,则可以比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); }
此处,CHandle
和CRequest
具有链接,因此可以在您的方法签名中使用; make
和get
的重载具有内部链接,因此可以与匿名类型进行交互。您可以将其放在标题中,甚至是static
函数。
您必须修改代码,以便在例如MyCls::myFct
调用库,使用get
包装参数并使用make
返回值。
答案 3 :(得分:0)
这似乎有效:
class MyCls {
public:
typedef _Opaque MHandle;
static void* myFct(MHandle handle) {
return 0;
}
};