简体   繁体   English

如何更新具有兼容性的 C/C++ 结构

[英]How to update a C/C++ struct with compatibility

I have a struct defined in a historical library with alas, an unfortunate choice of type: unsigned char * instead of char* .我在历史库中定义了一个struct ,唉,这是一个不幸的类型选择: unsigned char *而不是char*

struct MyStruct {

  unsigned char * myMember;
};

This struct is used by a huge number of C applications and, more and more, by C++ applications.大量 C 应用程序以及越来越多的 C++ 应用程序使用此struct Those C++ applications raise an error when used with strlen , for example, forcing us to cast.那些 C++ 应用程序在与strlen一起使用时会引发错误,例如,强制我们进行强制转换。 Lots of casts.很多演员。

I would like to remedy that.我想对此进行补救。

But one thing is extremely important: it must be absolutely compatible without modification and have no impact on the existing projects.但有一点极其重要:必须绝对兼容,无需修改,对现有项目无影响。

I thought about making a union .我想过建立一个union Alas, my variable can't have the same name.唉,我的变量不能有相同的名称。

struct MyStruct {

  union {
    unsigned char * myMember;
    // char * myMember; Obvioulsy, rejected by the compiler.
};

I'm afraid there isn't any obvious solution.恐怕没有任何明显的解决方案。 Am I wrong?我错了吗?

I can't change the size of the struct because it's mapped in an unchangeable sized shared memory.我无法更改结构的大小,因为它映射到大小不变的共享 memory 中。

Note: You can't solve this in C++, because strict C doesn't like unsigned char* either.注意:你不能在 C++ 中解决这个问题,因为严格的 C 也不喜欢unsigned char* Even with lax C compiler settings, you'd get (example with gcc default settings):即使使用松散的 C 编译器设置,您也会得到(例如 gcc 默认设置):

pointer targets in assignment differ in signedness [-Wpointer-sign]|赋值中的指针目标在符号上不同 [-Wpointer-sign]|

even when doing something basic such as simple assignment ms.myMember="hello";即使做一些基本的事情,比如简单的赋值ms.myMember="hello"; . .

Here is a C11 solution.这是 C11 解决方案。

struct MyStruct {
  union // anonymous union
  {
    unsigned char * myMember;
    char* myCharMember;
  };
};  

#define myMember myCharMember

Test code:测试代码:

struct MyStruct ms;  
_Generic(ms.myMember, 
         char*:           puts("I'm a char*"), 
         unsigned char*:  puts("I'm an unsigned char*"));

Without the #define and it will tell you "I'm an unsigned char*" , but with the #define , "I'm a char*" .如果没有#define ,它会告诉你"I'm an unsigned char*" ,但如果使用#define ,它会告诉你 " "I'm a char*"

There is a simpler remedy.有一个更简单的补救措施。 Often, when you are annoyed by having to do something too many times, it can simply be solved by doing that thing once within a function:通常,当您因为不得不多次执行某事而烦恼时,只需在 function 中执行一次即可轻松解决:

std::size_t unsigned_strlen(const unsigned char* str) noexcept
{
    return std::strlen(reinterpret_cast<const char*>(str));
}

There is no way you can make the myMember be a char* or any other type without potentially breaking existing projects, for the simple reason that its type can be deduced在不破坏现有项目的情况下,您无法使myMember成为char*或任何其他类型,原因很简单,可以推断出它的类型

void foo(decltype(MyStruct::myMember));

This will cause ABI issues, the mildest of which is a linker error.这将导致 ABI 问题,其中最轻微的是 linker 错误。

The half measure is to provide a shorthand for the cast一半的措施是为演员表提供速记

struct MyStruct
{
#ifdef __cplusplus
   char* signed_myMember() { return (char*)myMember; }
#endif
};

Fortunately, char is allowed to alias anything in C++, making this legal.幸运的是,允许char为 C++ 中的任何内容加上别名,这使得这变得合法。

Given your goal is to create an island of perfection and not fix the world, you could create an adapter with an implicit constructor from this old struct and use that adapter in the "island of perfection" code.鉴于您的目标是创建一个完美岛而不是修复世界,您可以使用这个旧结构的 隐式构造函数创建一个适配器,并在“完美岛”代码中使用该适配器。

struct BetterMyStruct {
  char* myMember;
  
  BetterMyStruct(MyStruct const& x): myMember((char*)x.myMember) {
    // any ugly secret hacks you want or need
  }

};

...

size_t better_strlen(BetterMyStruct const& x) {
  // perfect function
  return std::string_view(x.myMember).size();
}

...

better_strlen(MyStruct(...)); // perfect function invocation on imperfect inputs

Since you are allowed to change MyStruct I suggest just removing unsigned which would break the existing ABI and require recompilation of everything using the struct.由于您可以更改MyStruct我建议只删除unsigned这会破坏现有的 ABI 并需要使用该结构重新编译所有内容。

If that's not an option for some reason, you could inherit the original struct and provide a getter method for the C++ applications.如果由于某种原因这不是一个选项,您可以继承原始结构并为 C++ 应用程序提供一个 getter 方法。

// use this in C apps
struct MyStruct {
    unsigned char* myMember;
};

// use this in C++ apps
struct MyStructCpp : MyStruct {
    MyStructCpp(const MyStruct& rhs) : MyStruct(rhs) {}
    char* myMemberAsChar() const { return reinterpret_cast<char*>(myMember); }    
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM