自学内容网 自学内容网

CMU15445-2023Fall,Lab0 C++Primer 个人流程记录


零、前言

Project0 主要就是环境搭建,项目内容还是很简单的。

这里只是简略的记录一下过程,不会太详细,越来越忙了啊……


一、环境搭建

官方让用 Ubuntu22.04,这里我是 WSL + Ubuntu20.04,暂时能用出问题再说,

用WSL主要是不想在VMware上装CLion或者VScode了

VSCode 连接 WSL 装了4个插件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后 Ubuntu 上要装的各种工具我也记不太清了,一般遇到的时候 apt intall 也不晚

需要注意的是 CodeLLDB 这个插件挺逆天的,必须得通过VSIX装(至少我本地 和 WSL上都是)

一般就是你在VSCode上装完之后,过一会会报错,点击在URL上下载,然后自己想办法通过terminal 命令 或者 VSCode 在扩展那里点击 Install from VSIX 。

然后就是 把 bustub 那个仓库部署到本地了

在 CMU15445 那个 github 主页 跟着来就行 https://github.com/cmu-db/bustub

二、Project0 流程

字典树还是很简单的,关于字典树我也有博客:

[Trie树/字典树的原理及实现C/C++]_trie字典树原理-CSDN博客

2.1 TASK1

TASK1 就是字典树基操,字典树都大同小异,看一下它的 TrieNode 定义然后直接上手写就行

他只不过把指针换成了智能指针,这里也只涉及了 shared_ptr 和 unique_ptr,二者分别是引用计数法 和 禁止拷贝,也比较简单,我之前也有写博客:[让内存无处可逃:智能指针C++11]_接口用智能指针-CSDN博客

代码逻辑很简单,也不再说了,码风 是我个人码风 被提交时的格式检查 恶心之后改成这样的,可读性应该还能看吧……

因为比较简单,我也没写什么注释

Get

template <class T>
auto Trie::Get(std::string_view key) const -> const T * {
  auto cur = root_;
  for (char ch : key) {
    if (cur == nullptr || cur->children_.find(ch) == cur->children_.end()) {
      return nullptr;
    }
    cur = cur->children_.at(ch);
  }

  const auto *res = dynamic_cast<const TrieNodeWithValue<T> *>(cur.get());
  if (res != nullptr) {
    return res->value_.get();
  }

  return nullptr;
  // You should walk through the trie to find the node corresponding to the key. If the node doesn't exist, return
  // nullptr. After you find the node, you should use `dynamic_cast` to cast it to `const TrieNodeWithValue<T> *`. If
  // dynamic_cast returns `nullptr`, it means the type of the value is mismatched, and you should return nullptr.
  // Otherwise, return the value.
}

Put

template <class T>
auto Trie::Put(std::string_view key, T value) const -> Trie {
  // 如果在根插入,我们就要特判 TrieNodeWithValue 了
  if (key.empty()) {
    std::shared_ptr<T> node_value = std::make_shared<T>(std::move(value));
    // move: 因为 提示了 T 可能为 unique_ptr 类型
    std::shared_ptr<TrieNodeWithValue<T>> new_root = nullptr;

    if (root_->children_.empty()) {
      new_root = std::make_shared<TrieNodeWithValue<T>>(node_value);
    } else {
      new_root = std::make_shared<TrieNodeWithValue<T>>(root_->children_, node_value);
    }
    return Trie(std::move(new_root));
  }

  std::shared_ptr<TrieNode> new_root = nullptr;

  if (root_ == nullptr) {
    new_root = std::make_shared<TrieNode>();
  } else {
    new_root = root_->Clone();
  }

  std::shared_ptr<TrieNode> cur = new_root;

  for (size_t i = 0; i + 1 < key.size(); ++i) {
    char c = key.at(i);
    auto it = cur->children_.find(c);
    if (it == cur->children_.end()) {
      auto ptr = std::make_shared<TrieNode>();
      cur->children_.insert({c, ptr});
      cur = ptr;
    } else {
      std::shared_ptr<TrieNode> ptr = it->second->Clone();
      it->second = ptr;
      cur = ptr;
    }
  }

  char c = key.back();
  auto it = cur->children_.find(c);
  if (it == cur->children_.end()) {
    std::shared_ptr<T> val_p = std::make_shared<T>(std::move(value));
    cur->children_.insert({c, std::make_shared<TrieNodeWithValue<T>>(val_p)});
  } else {
    std::shared_ptr<T> val_p = std::make_shared<T>(std::move(value));
    it->second = std::make_shared<TrieNodeWithValue<T>>(it->second->children_, val_p);
  }

  return Trie(new_root);
}

Remove

auto Trie::Remove(std::string_view key) const -> Trie {
  if (this->root_ == nullptr) {
    return *this;
  }

  // 同样特判键为空
  if (key.empty()) {
    // TreeNodeWithValue
    if (root_->is_value_node_) {
      if (root_->children_.empty()) {
        return Trie(nullptr);
      }
      std::shared_ptr<TrieNode> new_root = std::make_shared<TrieNode>(root_->children_);
      return Trie(new_root);
    }
    // 否则直接返回
    return *this;
  }

  std::shared_ptr<TrieNode> new_root = root_->Clone();

  auto dfs = [&](auto &&self, const std::shared_ptr<TrieNode> &cur, size_t i) -> bool {
    char c = key.at(i);
    auto it = cur->children_.find(c);
    if (it == cur->children_.end()) {
      return false;
    }
    if (i + 1 == key.size()) {
      if (!it->second->is_value_node_) {
        return false;
      }

      if (it->second->children_.empty()) {
        cur->children_.erase(it);
      } else {
        it->second = std::make_shared<TrieNode>(it->second->children_);
      }
      return true;
    }
    std::shared_ptr<TrieNode> ptr = it->second->Clone();
    if (!self(self, ptr, i + 1)) {
      return false;
    }
    if (ptr->children_.empty() && !ptr->is_value_node_) {
      cur->children_.erase(it);
    } else {
      it->second = ptr;
    }
    return true;
  };

  if (dfs(dfs, new_root, 0)) {
    if (new_root->children_.empty() && !new_root->is_value_node_) {
      new_root = nullptr;
    }
    return Trie(new_root);
  }

  return *this;
  // You should walk through the trie and remove nodes if necessary. If the node doesn't contain a value any more,
  // you should convert it to `TrieNode`. If a node doesn't have children any more, you should remove it.
}

2.2 TASK2

TASK2 就更简单了,如果有学过 操作系统的话,无非就是经典的 读者写者操作

项目那里说是 多读者 单写者,那么为了保证线程安全,我们上锁即可

后续我可能会写博客记录下 C++并发实战?

Get

template <class T>
auto TrieStore::Get(std::string_view key) -> std::optional<ValueGuard<T>> {
  // Pseudo-code:
  // (1) Take the root lock, get the root, and release the root lock. Don't lookup the value in the
  //     trie while holding the root lock.
  // (2) Lookup the value in the trie.
  // (3) If the value is found, return a ValueGuard object that holds a reference to the value and the
  //     root. Otherwise, return std::nullopt.

  std::lock_guard<std::mutex> lock(root_lock_);
  auto trie = root_;
  const T *val = trie.Get<T>(key);
  if (val == nullptr) {
    return std::nullopt;
  }

  return ValueGuard<T>(trie, *val);
}

Put

template <class T>
void TrieStore::Put(std::string_view key, T value) {
  // You will need to ensure there is only one writer at a time. Think of how you can achieve this.
  // The logic should be somehow similar to `TrieStore::Get`.

  std::lock_guard<std::mutex> lock(write_lock_);

  auto new_trie = root_.Put<T>(key, std::move(value));
  std::lock_guard<std::mutex> root_lock(root_lock_);
  root_ = new_trie;
}

Remove

void TrieStore::Remove(std::string_view key) {
  // You will need to ensure there is only one writer at a time. Think of how you can achieve this.
  // The logic should be somehow similar to `TrieStore::Get`.

  std::lock_guard<std::mutex> lock(write_lock_);

  auto new_trie = root_.Remove(key);
  std::lock_guard<std::mutex> root_lock(root_lock_);
  root_ = new_trie;
}

2.3 TASK3

emm

前面安装了 LLDB,所以这里直接调试去看即可

LLDB 那个 json 文件 只需要写一下路径就行

#include "primer/trie.h"

// TODO(student): Equinox

const uint32_t CASE_1_YOUR_ANSWER = 10;
const uint32_t CASE_2_YOUR_ANSWER = 1;
const uint32_t CASE_3_YOUR_ANSWER = 25;

2.4 TAKS4

这个就是让你写个大小写转换函数,也没什么说的

  auto Compute(const std::string &val) const -> std::string {
    // TODO(student): implement upper / lower.
    std::string res;
    switch (expr_type_) {
      case StringExpressionType::Lower:
        for (auto ch : val) {
          res.push_back(std::tolower(ch));
        }
        break;
      case StringExpressionType::Upper:
        for (auto ch : val) {
          res.push_back(std::toupper(ch));
        }
        break;
      default:
        break;
    }
    return res;
  }
// NOLINTNEXTLINE
auto Planner::GetFuncCallFromFactory(const std::string &func_name, std::vector<AbstractExpressionRef> args)
    -> AbstractExpressionRef {
  // 1. check if the parsed function name is "lower" or "upper".
  // 2. verify the number of args (should be 1), refer to the test cases for when you should throw an `Exception`.
  // 3. return a `StringExpression` std::shared_ptr.
  if ((func_name == "lower" || func_name == "upper") && args.size() == 1) {
    return static_cast<std::shared_ptr<StringExpression>>(std::make_shared<StringExpression>(
        args[0], func_name == "lower" ? StringExpressionType::Lower : StringExpressionType::Upper));
  }
  throw Exception(fmt::format("func call {} not supported in planner yet", func_name));
}

2.5 本地测试

TASK1

在 build 文件夹下

# make -j12 trie_test
# ./test/trie_test

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码没问题的话就是这样子

TASK2

# make -j12 trie_store_test
# ./test/trie_store_test

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

TASK3 和 TASK4 我这里本地测会有点问题但是提交没问题,很怪,就不放了。

2.6 提交

实验平台:https://www.gradescope.com/

2023Fall的码:KK5DVJ

自行创建账号

然后就是最恶心的部分了,提交前的格式检查

# sudo apt-get install clang-format
# apt-get install clang-tidy

然后在build文件夹下:

# make format
# make check-lint
# make check-clang-tidy-p0

然后回到主目录,即 build 的上一级目录,这里调用一下 py 脚本

# python3 gradescope_sign.py

第一次调用的话就是各种signature,无脑确认就行

2.7 个人提交时遇到的问题

  1. 我遇到的第一个问题: make format 会告诉我 一个 什么 全是大写的文件不存在,我忘了具体是什么了,最后是重新在 build 文件夹下执行最初的初始化操作后就没事了(就是 github主页那里写的那个)
  2. 第二个问题:码风问题。执行 make check-clang-tidy-p0 后告诉我仨cpp文件 failed,其实是格式不对,提交上去全pass了还是0分,最后发现 平台上 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    这个测试点会有 具体的格式错误的信息,照着改(比如文件结尾必须有一行空行啊,所有语句块就算一行也得用 花括号啊,驼峰命名让你加下划线啊之类的)

这两个问题耗我时间最长,别的问题我都忘了哈铪,可以自行搜索。

提交测完是这个样子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


原文地址:https://blog.csdn.net/EQUINOX1/article/details/142740875

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!