转换具有某些共同成员的结构

Cast structs with certain common members

假设我有 2 个 structs:

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();
}

编辑:

在 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);
}

I want to avoid someFunction() to be template (just to avoid this kind of things)

Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?

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

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

这是一个example

std::variant 知道其中存储的是什么类型,您可以随时使用 visit 来使用其中的任何成员(为清楚起见,此处为片段):

// 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;
};

有了它,你可以声明一个基础 class 指针,分配它,并在 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

关于我的评论的解释:“它有效,但除非编译器真的很聪明并且可以优化内部循环中的虚拟调度,否则它会比你实际花时间慢输入 cast":

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