c++ - 简单虚拟机安全、高效的底层数据类型

标签 c++

前段时间我创建了一个简单的模拟计算机。它有外围设备、可以渲染为 OpenGL 纹理的屏幕缓冲区,以及其他一些简洁的功能。它运行良好,运行良好,总的来说我很满意。

除了,我作弊了。

底层数据类型是整数、 float 和指令类型的 union (拆分为位字段)。

对于任何正确的(模拟的)程序, union 总是被安全地使用,只从写入的最后一个 union 成员读取。但是,格式错误的程序(例如从模拟硬盘驱动器加载)可能会乱序访问成员,这可能会使我面临与 union 滥用相关的常见问题:

  • 可以在编译时优化写入的可能性——编译器可能没有足够的信息来尝试这种优化
  • 从 union 中读取的值可能是垃圾 - 这对我来说是完全可以接受的行为。
  • 以这种方式读取的 float 可能是信号 NaN/陷阱值 - 这是一个真正的问题 - 使模拟计算机崩溃没问题,但使真实程序崩溃是一场灾难。
  • 这在技术上是未定义的行为,所以虽然它可能不会,但它可能会点燃计算机、删除我的硬盘驱动器或召唤 Cthulhu。

考虑的解决方案:

  • 坚持联盟 - 也许它对于所有现实世界的平台都有足够明确的定义?也许有清理 sNaN 的方法?
  • Tagged union - 将有效地减少一半的内存空间
  • 单独存储高效打包标签数组 - 标签传播有点繁琐,但在其他方面还是可行的。
  • char 数组 - 看似简单,但安全地执行它的成本,允许从与写入的类型不同的类型读取,确实加起来。
  • 整数类型 - 与上面的 float 和指令相同,区别在于整数是微不足道的。
  • char 数组加上单独的整数和浮点寄存器 - 很有特点并且在很多方面都很理想,但需要我编写一个可以有效使用这些的编译器。

我想这是许多 SO 用户曾经或多次尝试过的项目,因此特别欢迎有特定问题的经验。

最佳答案

如果您的编译器支持,您可以使用 C++17 std::variant (基于 boost::variant )。


编辑:为了最大限度地节省空间,选择加入类型的安全性,您可以按照以下方式做一些事情

union Word { int32_t i; float f; Instruction inst; };

namespace MemAccess
{
        static std::bitset<MEM_SIZE> int32_whitelist,
                                     float_whitelist,
                                     inst_whitelist;
        static std::array<Word, MEM_SIZE> memory;
        // set or reinterpret as int32
        int32_t &
        int32_at(const size_t at)
        {
                int32_whitelist[at] = 1;
                float_whitelist[at] = inst_whitelist[at] = 0;

                return memory[at].i;
        }
        // interpret as int32 only if whitelisted
        int32_t &
        int32_checked(const size_t at)
        {
                if (int32_whitelist[at])
                {
                        return memory[at].i;
                }
                else
                {
                        throw;
                }
        }
        // equivalent functions for floats and instructions
}

编辑 2:我发现这也可以用一个位集来完成。

static std::array<Word, MEM_SIZE> memory;
static std::bitset<MEM_SIZE * 2> whitelist;

float &
float_at(const size_t at)
{       // None = 00, Inst = 10, Int32 = 11
        whitelist[at * 2]     = 0;
        whitelist[at * 2 + 1] = 1;

        return memory[at].f;
}

float &
float_checked(const size_t at)
{
        if (!whitelist[at * 2] && whitelist[at * 2 + 1])
        {
                return memory[at].f;
        }

        throw;
}

关于c++ - 简单虚拟机安全、高效的底层数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39338875/

相关文章:

c++ - 为什么 atomic_flag.clear() 有一个次优的默认 memory_order 参数?

c++ - 如何遍历包含 3 个值的二维数组并检查这些值?

c++ - 如何安全地关闭其中有无限循环的线程

c++ - thread_local "storage class specified"

c++ - 移动头文件中的函数以获得干净的排序

c++ - 抛出异常时使用 cout 语句尝试阻止行为

C++ 错误 : no matching constructor for initialization of

c++ - 接受用户输入并显示它的程序问题

c++ - 为什么模板及其模板成员的模板参数列表不能合并?

c++ - 如何模拟方法模板的虚拟性