具有某些公共成员的强制转换结构

问题描述

假设我有 2 个struct

typedef struct
{
    uint8_t useThis;            
    uint8_t u8Byte2;             
    uint8_t u8Byte3; 
    uint8_t u8Byte4;  
} tstr1

typedef struct
{
    uint8_t u8Byte1;            
    uint8_t u8Byte2;             
    uint8_t useThis;  
} tstr2

我将需要函数内的 useThis 成员,但在某些情况下,我需要强制转换一个或另一个结构:

void someFunction()
{
    someStuff();
    SOMETHING MyInstance;
    if(someVariable)
    {
        MyInstance = reinterpret_cast<tstr1*>(INFO_FROM_HARDWARE); //This line of course doesn't work
    }
    else
    {
        MyInstance = reinterpret_cast<tstr2*>(INFO_FROM_HARDWARE); //This line of course doesn't work
    }
    MyInstance->useThis; //Calling this memeber with no problem

    moreStuff();
}
  • 所以我想使用 useThis 无论做了什么演员。这怎么办?

  • 我想避免 someFunction() 成为模板(只是为了避免 this kind of things

  • 注意像 this 这样的问题有一种类似的问题,但结构成员有相同的顺序

编辑:

在 RealLife 中,这些结构体要大得多,并且有几个“同名”成员。直接将 uint8_t 转换为 reinterpret_cast<tstr1*>(INFO_FROM_HARDWARE)->useThis 会很乏味,并且需要几个 reinterpret_cast(虽然这是我在此编辑之前的问题的有效解决方案)。这就是我坚持MyInstance“完整”的原因。

解决方法

这就是模板的用途:

template<class tstr>
std::uint8_t
do_something(std::uint8_t* INFO_FROM_HARDWARE)
{
    tstr MyInstance;
    std::memcpy(&MyInstance,INFO_FROM_HARDWARE,sizeof MyInstance);
    MyInstance.useThis; //Calling this memeber with no problem
    // access MyInstance within the template
}

// usage
if(someVariable)
{
    do_something<tstr1>(INFO_FROM_HARDWARE);
}
else
{
    do_something<tstr2>(INFO_FROM_HARDWARE);
}

我想避免 someFunction() 成为模板(只是为了避免这种事情)

为什么我不能将模板类的定义与其声明分开并将其放入 .cpp 文件中?

链接问题对您的用例来说不是问题,因为潜在的模板参数集是一个有限集。下一个常见问题解答 entry 解释了如何: 使用模板的显式实例。

,

按照 AndyG 的建议,std::variant 怎么样(没有提到您正在使用的 c++ 标准,所以也许 c++17 解决方案是可以的 - 如果没有 c+,也值得使用 +17 可用)。

这是一个example

std::variant 知道其中存储的是什么类型,并且您可以随时使用访问来使用其中的任何成员(为了清楚起见,请在此处摘录):

// stolen from @eerrorika (sorry for that :( )
struct hardware {
    uint8_t a = 'A';
    uint8_t b = 'B';
    uint8_t c = 'C';
    uint8_t d = 'D';
};

struct tstr1 {
    uint8_t useThis;            
    uint8_t u8Byte2;             
    uint8_t u8Byte3; 
    uint8_t u8Byte4;  
};

struct tstr2 {
    uint8_t u8Byte1;            
    uint8_t u8Byte2;             
    uint8_t useThis;  
};

// stuff

if(true)
{
    msg = *reinterpret_cast<tstr1*>(&hw);
} 
else
{
    msg = *reinterpret_cast<tstr2*>(&hw);
}

std::visit(overloaded {
    [](tstr1 const& arg) { std::cout << arg.useThis << ' '; },[](tstr2 const& arg) { std::cout << arg.useThis << ' '; }
},msg);

编辑:你也可以做一个指针的变体 EDIT2:忘记转义一些东西...

,

对结构成员的简单引用可能就是您所需要的:

uint8_t &useThis=SomeVariable
 ?reinterpret_cast<tstr1*>(INFO_FROM_HARDWARE)->useThis
 :reinterpret_cast<tstr2*>(INFO_FROM_HARDWARE)->useThis;
,

在映射到硬件时,使用 virtual 调度通常不是您想要的,但它是一种替代方法。

示例:

// define a common interface
struct overlay_base {
    virtual ~overlay_base() = default;
    virtual uint8_t& useThis() = 0;
    virtual uint8_t& useThat() = 0;
};

template<class T>
class wrapper : public overlay_base {
public:
    template<class HW>
    wrapper(HW* hw) : instance_ptr(reinterpret_cast<T*>(hw)) {}
    uint8_t& useThis() { return instance_ptr->useThis; }
    uint8_t& useThat() { return instance_ptr->useThat; }

private:
    T* instance_ptr;
};

有了它,您可以声明一个基类指针,分配它,并在 if 语句之后使用:

int main(int argc,char**) {

    std::unique_ptr<overlay_base> MyInstance;

    if(argc % 2) {
        MyInstance.reset( new wrapper<tstr1>(INFO_FROM_HARDWARE) );
    } else {
        MyInstance.reset( new wrapper<tstr2>(INFO_FROM_HARDWARE) );
    }

    std::cout << MyInstance->useThis() << '\n';
    std::cout << MyInstance->useThat() << '\n';
}

Demo

关于我的评论的解释:“它可以工作,但是除非编译器非常聪明并且可以优化内部循环中的虚拟分派,否则它会比您实际花时间进行类型转换要慢":

virtual 分派视为在运行时使用的查找表 (vtable)(这通常是实际发生的情况)。在调用 virtual 函数时,程序必须使用该查找表来查找要调用的实际成员函数的地址。当无法优化查找时(正如我在上面的示例中通过使用仅在运行时可用的值确保的那样),与通过静态转换获得的相比,它确实需要额外的几个 CPU 周期。