将继承的专用模板类型存储到共享指针的向量中?

时间:2019-05-10 06:37:57

标签: c++ inheritance class templates specialization

我正在研究将在虚拟PC应用程序中使用的Register类。我正在尝试使它们尽可能基本且快速,因此它们应该至少可以是默认可构造的,并且至少可以是默认可复制的。

我的寄存器类型将依赖typedef作为其类型:

注册。h

#pragma once

#include <vector>
#include <memory>

typedef std::uint8_t u8;
typedef std::uint16_t u16;
typedef std::uint32_t u32;
typedef std::uint64_t u64;

struct Register {
    virtual void* getValue() = 0;
};

template<typename T>
struct Register_ty : Register {
    void* data_;
    explicit Register_ty(T* data) : data_(data) {}

    virtual void* getValue() = 0;
};

template<typename T>
struct Register_t : Register_ty<T> {};

template<>
struct Register_t<u8> : Register_ty<u8> {
    explicit Register_t(u8* data) : Register_ty<u8>( data ) {}
    void* getValue() override {
        return data_;
    }
};

template<>
struct Register_t<u16> : Register_ty<u16> {
    explicit Register_t(u16* data) : Register_ty<u16>(data) {}
    void* getValue() override {
        return data_;
    }
};

template<>
struct Register_t<u32> : Register_ty<u32> {
    explicit Register_t(u32* data) : Register_ty<u32>(data) {}
    void* getValue() {
        return data_;
    }
};

template<>
struct Register_t<u64> : Register_ty<u64> {
    explicit Register_t(u64* data) : Register_ty<u64>(data) {}
    void* getValue() {
        return data_;
    }
};

struct Memory {
    std::vector<Register*> bank; // vector
}; 

当我像这样使用它时:

#include <iostream>
#include "Register.h"

int main() {
u8 a = 8;
Register_t<u8> r8(&a);

u16 b = 16;
Register_t<u16> r16(&b);

u32 c = 32;
Register_t<u32> r32(&c);

u64 d = 64;
Register_t<u64> r64(&d);

Memory mem;
mem.bank.push_back( &r8 );
mem.bank.push_back( &r16 );
mem.bank.push_back( &r32 );
mem.bank.push_back( &r64 );

for (auto& b : mem.bank) {
    std::cout << b->getValue() << '\n';
}

    return EXIT_SUCCESS;
}

此时一切似乎都还可以,这给了我一个可能的输出:

000000000029F6B4
000000000029F704
000000000029F754
000000000029F7A8

哪个给我指针的地址。但是,当我尝试这样取消对它们的引用时:

for ( auto& b : mem.bank ) {
    std::cout << *b->getValue() << '\n';
}

这无法编译,因此我不得不回过头来重新考虑我的代码...到目前为止,我想到了这一点:

#include <iostream>
#include "Register.h"

int main() {
    using namespace vpc;

    u8* pa = nullptr;
    u8 a = 8;
    pa = &a;
    Register_t<u8> r8(pa);

    u16* pb = nullptr;
    u16 b = 16;
    pb = &b;
    Register_t<u16> r16(pb);

    u32* pc = nullptr;
    u32 c = 32;
    pc = &c;
    Register_t<u32> r32(pc);

    u64* pd = nullptr;
    u64 d = 64;
    pd = &d;
    Register_t<u64> r64(pd);

    Memory mem;
    mem.bank.push_back( &r8 );
    mem.bank.push_back( &r16 );
    mem.bank.push_back( &r32 );
    mem.bank.push_back( &r64 );

    for (auto& b : mem.bank) {
        std::cout << b->getValue() << '\n';
    }

    std::cout << '\n';

    for (auto& b : mem.bank) {
        auto p = b->getValue();
        auto res = reinterpret_cast<u8*>(p);

        std::cout << static_cast<u16>(*res) << '\n';
    }
    return EXIT_SUCCESS;
};

它给了我输出:

00000000001DF694
00000000001DF704
00000000001DF774
00000000001DF7E8

8
16
32
64

现在给我正确的值。这是我第一次能够实现此行为,因此不得不使用pointer manipulation来实现。最初,我根本不使用指针,也不在类中使用void*

然后我不能只创建一个变量并通过它的地址传递它,因为那也不起作用,所以我不得不在我的主代码中创建那些类型的变量并用一些东西对其进行初始化,我必须创建一个指针这些类型,并使用nullptr对其进行初始化,最后我不得不将指针分配给它们各自的变量类型地址。

然后要能够通过取消对它们的引用来从这些指针中检索实际值,我必须通过强制转换进行一些指针操作,然后必须强制转换该取消引用的类型。

for (auto& b : mem.bank) {
    // create an auto p and get the value; p should be a void*
    auto p = b->getValue();

    // create an auto type for our result and cast our
    // void* p to a u8* {smallest type} and {char} based
    auto res = reinterpret_cast<u8*>(p);

    // now since the underlying type is char I have to deference my
    // res* and static cast it to a u16. 
    std::cout << static_cast<u16>(*res) << '\n';
}

这种技巧不是很漂亮也不友好。到目前为止,它仍然有效,但是我已经提前看到了一些问题。如果我有一个u64类型的寄存器,并且它的大部分位都填充了需要的数据,并且我重新解释了它的指向u8 *的指针,请取消引用并将其转换为u16类型 我将丢失信息,但这是唯一为我提供正确结果的组合。

关于此代码设计或实现:

  • 有没有更简单,更清洁的方法来实现这一目标?
  • 在此特定实施中,数据丢失有多重要?
  • 您是否可能还会看到其他一些我看过的问题?

我想知道我该如何去做,因为我还没有尝试将这些寄存器类型存储为vector中的shared_ptr……而且所有这些转换和指针的使用都可以,但是看起来很丑陋,容易出现将来的问题。

0 个答案:

没有答案