首页 > 解决方案 > 将一组集合转换为单个范围

问题描述

我在创建某种类型的范围时遇到了麻烦。

在 LLVM(编译器基础设施)中,a Module(可能是翻译单元的代码)的组织方式如下:

llvm::Module m{...}; // A module could be a single translation unit.
for(const auto &func : m){
  for(const auto &basic_block : func){
    for(const auto &inst : basic_block){
    }
  }
}

在我正在处理的代码中,拥有一个创建包含在模块或函数中的一系列指令的函数非常方便。例如:

llvm::Module m;
llvm::Function f;
InstSet temp;
temp.insert(instructions(m));
temp.insert(instructions(f));

但是,我无法优雅地实现范围。我尝试过的事情:

  1. 实现自定义迭代器。事实证明它真的很乱,即使我想做的事情应该很简单......
  2. 使用协程实现。虽然效果稍微好一些,但性能却令人无法接受(调查后慢了大约 10 倍)。

我的问题是,有没有我遗漏的优雅解决方案?如果没有,我想知道为什么要理解我试图以错误方式解决的问题。

编辑(我在下面添加了自己的实现):

这是使用协程的实现(已完成):

struct InstructionsGenerator {
    struct promise_type;
    using Handle = coroutine_handle<promise_type>;
    struct promise_type {
        const Instruction *i;
        auto get_return_object() -> InstructionsGenerator { return {Handle::from_promise(*this)}; }
        auto yield_value(const Instruction &y) -> suspend_always {
            i = &y;
            return {};
        }
        auto initial_suspend() -> suspend_never { return {}; };
        auto final_suspend() -> suspend_always { return {}; };
        auto return_void() -> void {}
    };

    Handle h;
};

auto instructions_generator(const Module &m) -> InstructionsGenerator {
    for (const auto &f : m) {
        for (const auto &bb : f) {
            for (const auto &i : instructions(bb)) {
                co_yield i;
            }
        }
    }
}

struct InstructionRange {
    struct iterator {
        optional<InstructionsGenerator> g;

        auto operator++() -> auto & {
            g->h();
            return *this;
        }
        auto operator*() const -> auto & { return *(g->h.promise().i); }
        auto operator==(const iterator & /*unused*/) const -> bool { return g->h.done(); }
    };
    iterator b;

    [[nodiscard]] auto begin() const -> iterator { return b; }
    [[nodiscard]] auto end() const -> iterator { return {nullopt}; };

    ~InstructionRange() {
        if (b.g) {
            b.g->h.destroy();
        }
    }
    InstructionRange(InstructionsGenerator fi) : b{fi} {}
    InstructionRange(InstructionRange &&r) noexcept : b{r.b} { r.b.g = nullopt; }
};

auto instructions(const Module &m) -> InstructionRange { return {instructions_generator(m)}; };

这是使用自定义迭代器的实现(未完成。在意识到它太麻烦后放弃了它。):

// The real implementation was templated but to give you the idea, I've simplified it.
struct LiftedRange {
  llvm::Module& m;

  public:
    struct iterator {
      llvm::Module& m;
      llvm::Module::iterator l1;
      llvm::Function::iterator l2;

      auto operator*(); // Skipped for brevity.
      auto operator==(); // Skipped for brevity.
      auto operator++(){
        if(next(l2) != l1.end()){
          ++l2;
          return *this;
        }
        do {
          ++l1;
        while(l1->empty() || l1 != m.end()); // Avoid empty sets.
        l2 = l1->begin();
        return *this;
      }
    };
    
    auto begin(); // Skipped for brevity.
    auto end(); // Skipped for brevity.
};

标签: c++iteratorcoroutine

解决方案


推荐阅读