Skip to content

Commit

Permalink
Dev (#4)
Browse files Browse the repository at this point in the history
Set devcontainer.
Add test scenarios.
Set exception safety.
  • Loading branch information
andreiavrammsd authored Sep 1, 2024
1 parent 9db3f97 commit a7c4da5
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ IndentWidth: 4
Language: Cpp
PointerAlignment: Left
BreakBeforeBraces: Stroustrup
ColumnLimit: 120
ColumnLimit: 120
51 changes: 51 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Use the official Ubuntu base image
FROM ubuntu:20.04

# Avoid interactive package installation
ENV DEBIAN_FRONTEND=noninteractive

# Install required packages
RUN apt update && apt install -y \
build-essential \
cmake \
clang-tidy \
clang-format \
lcov \
git \
curl \
wget \
unzip \
python3 \
python3-pip \
gdb \
lldb \
clang \
llvm \
nano \
software-properties-common \
&& rm -rf /var/lib/apt/lists/*

# Create a non-root user
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN groupadd -g $USER_GID $USERNAME || true \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /bin/bash \
&& apt update \
&& apt install -y sudo \
&& echo $USERNAME ALL=\(ALL\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME

# History
RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
&& mkdir /commandhistory \
&& touch /commandhistory/.bash_history \
&& chown -R $USERNAME /commandhistory \
&& echo "$SNIPPET" >> "/home/$USERNAME/.bashrc"

# Set the user to vscode
USER $USERNAME

# Set the workspace directory
WORKDIR /workspace
31 changes: 31 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "C++ Development with CMake and Bazel",
"dockerFile": "Dockerfile",
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cmake-tools",
"ms-vscode.cpptools",
"ms-vscode.cpptools-extension-pack",
"xaver.clang-format",
"twxs.cmake",
"fredericbonnet.cmake-test-adapter",
"ms-vscode.cmake-tools",
"notskm.clang-tidy",
"ryanluker.vscode-coverage-gutters",
]
}
},
"mounts": [
"source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"source=${env:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached",
"source=history,target=/commandhistory,type=volume"
],
"workspaceFolder": "/workspace",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined"
],
"remoteUser": "vscode"
}
2 changes: 1 addition & 1 deletion .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc-11",
"compilerPath": "/usr/bin/gcc-9",
"cStandard": "c17",
"intelliSenseMode": "linux-clang-x64",
"configurationProvider": "ms-vscode.cmake-tools"
Expand Down
9 changes: 1 addition & 8 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
{
"recommendations": [
"ms-vscode.cpptools",
"ms-vscode.cpptools-extension-pack",
"xaver.clang-format",
"twxs.cmake",
"fredericbonnet.cmake-test-adapter",
"ms-vscode.cmake-tools",
"notskm.clang-tidy",
"ryanluker.vscode-coverage-gutters",
"ms-vscode-remote.remote-containers",
]
}
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
"coverage.cobertura.xml"
],
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.codeAnalysis.clangTidy.enabled": true,
"C_Cpp.codeAnalysis.clangTidy.useBuildPath": true,
"clang-tidy.buildPath": "build",
"C_Cpp.codeAnalysis.clangTidy.args": [
"-p=build"
],
"files.associations": {
"array": "cpp",
"*.tcc": "cpp",
Expand Down
16 changes: 13 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"command": "mkdir -p build",
"group": {
"kind": "build",
"isDefault": true
"isDefault": false
},
"options": {
"cwd": "${workspaceFolder}"
Expand All @@ -21,7 +21,7 @@
"dependsOn": "Create build directory",
"group": {
"kind": "build",
"isDefault": true
"isDefault": false
},
"options": {
"cwd": "${workspaceFolder}/build"
Expand All @@ -41,6 +41,16 @@
"cwd": "${workspaceFolder}/build"
},
"problemMatcher": []
}
},
{
"label": "Run clang-tidy on current file",
"type": "shell",
"command": "clang-tidy",
"args": [
"${file}",
"-p=build"
],
"problemMatcher": ["$gcc"]
}
]
}
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,35 @@ int main() {

```

See [tests](tests/zip_test.cpp).
See [tests](tests/).

## Development

### Tools
* [SSH keys for GitHub](https://help.ubuntu.com/community/SSH/OpenSSH/Keys) (~/.ssh)
* [VS Code](https://code.visualstudio.com/) (see [.vscode/extensions.json](.vscode/extensions.json))
* [Docker](https://docs.docker.com/engine/install/ubuntu/)

### Actions
* Debug: F5
* Coverage: Ctrl + Shift + P -> Run Task -> Generate Coverage Report
* Show coverage inline: Ctrl + Shift + 7 OR Ctrl + Shift + P -> Coverage Gutters: Display Coverage
* Coverage as HTML: See build/coverage_html/index.html

## TODO

* Write size() as end() - begin()
* Exception guarantees
* constexpr
* const correctness
* Write benchmarks
* ContainersAndAlgorithms test fails at `EXPECT_EQ(it, std::prev(const_zip.end()));` for std::list
* Can the zip iterator really be bidirectional?
* Document or conditionaly set the iterator tag by the containers types
* Consider checked access that returns an optional reference
* List tools required for dev or create devcontainer
* Do not allow to mix begin/end with cbegin/cend
* Run clang-tidy, clang-format in CI/file save
* Test
* 100% coverage
* Analyze if LCOV_EXCL_LINE is needed
* Finish tests
* With std algorithms
* Entire API in non-const and const context
Expand Down
52 changes: 37 additions & 15 deletions include/msd/zip.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,77 @@ class zip_iterator {
using pointer = std::tuple<typename std::iterator_traits<Iterators>::pointer...>;
using reference = std::tuple<typename std::iterator_traits<Iterators>::reference...>;

explicit zip_iterator(Iterators... iterators) : iterators_{iterators...} {}
explicit zip_iterator(Iterators... iterators) noexcept : iterators_{iterators...} {}

value_type operator*() const { return dereference(std::index_sequence_for<Iterators...>{}); }
value_type operator*() const noexcept { return dereference(std::index_sequence_for<Iterators...>{}); }

bool operator==(const zip_iterator& other) const { return equal(std::index_sequence_for<Iterators...>{}, other); }
bool operator==(const zip_iterator& other) const noexcept
{
return equal(std::index_sequence_for<Iterators...>{}, other);
}

bool operator!=(const zip_iterator& other) const { return !equal(std::index_sequence_for<Iterators...>{}, other); }
bool operator!=(const zip_iterator& other) const noexcept
{
return !equal(std::index_sequence_for<Iterators...>{}, other);
}

zip_iterator& operator++() noexcept
{
advance(std::index_sequence_for<Iterators...>{}, 1);
return *this;
}

zip_iterator operator+(const std::size_t offset) const noexcept
{
auto iterator = *this;
iterator.advance(std::index_sequence_for<Iterators...>{}, offset);
return iterator;
}

zip_iterator operator+(const zip_iterator& other) const noexcept
{
auto iterator = *this;
const auto distance = std::distance(iterator, other);
iterator.advance(std::index_sequence_for<Iterators...>{}, distance);
return iterator;
}

zip_iterator& operator--() noexcept
{
advance(std::index_sequence_for<Iterators...>{}, -1);
return *this;
}

zip_iterator operator+(const std::size_t offset) const noexcept
zip_iterator operator-(const int offset) const noexcept
{
auto iterator = *this;
iterator.advance(std::index_sequence_for<Iterators...>{}, offset);
iterator.advance(std::index_sequence_for<Iterators...>{}, -offset);
return iterator;
}

zip_iterator operator-(const int offset) const noexcept
zip_iterator operator-(const zip_iterator& other) const noexcept
{
auto iterator = *this;
iterator.advance(std::index_sequence_for<Iterators...>{}, -offset);
const auto distance = std::distance(other, iterator);
iterator.advance(std::index_sequence_for<Iterators...>{}, -distance);
return iterator;
}

private:
template <std::size_t... I>
value_type dereference(std::index_sequence<I...>) const
value_type dereference(std::index_sequence<I...>) const noexcept
{
return std::tie(*std::get<I>(iterators_)...);
}

template <std::size_t... I>
bool equal(std::index_sequence<I...>, const zip_iterator& other) const
bool equal(std::index_sequence<I...>, const zip_iterator& other) const noexcept
{
return ((std::get<I>(iterators_) == std::get<I>(other.iterators_)) || ...);
}

template <std::size_t... I>
void advance(std::index_sequence<I...>, int offset)
void advance(std::index_sequence<I...>, const int offset) noexcept
{
((std::advance(std::get<I>(iterators_), offset)), ...);
}
Expand All @@ -86,7 +108,7 @@ class zip {

using value_type = typename iterator::value_type;

explicit zip(Containers&... containers) : containers_{containers...} {}
explicit zip(Containers&... containers) noexcept : containers_{containers...} {}

iterator begin() const { return begin_impl(std::index_sequence_for<Containers...>{}); }
iterator end() const { return end_impl(std::index_sequence_for<Containers...>{}); }
Expand All @@ -108,7 +130,7 @@ class zip {

value_type front() const
{
assert(!empty());
assert(!empty()); // LCOV_EXCL_LINE
return *begin();
}

Expand All @@ -132,13 +154,13 @@ class zip {

private:
template <std::size_t... I>
iterator begin_impl(std::index_sequence<I...>) const
iterator begin_impl(std::index_sequence<I...>) const noexcept
{
return iterator{std::get<I>(containers_).begin()...};
}

template <std::size_t... I>
iterator end_impl(std::index_sequence<I...>) const
iterator end_impl(std::index_sequence<I...>) const noexcept
{
return std::next(iterator{std::get<I>(containers_).begin()...}, size());
}
Expand Down
32 changes: 29 additions & 3 deletions tests/zip_integration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
#include <forward_list>
#include <iterator>
#include <list>
#include <map>
#include <numeric>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
Expand All @@ -19,7 +22,7 @@ TEST(ZipIntegrationTest, ContainersAndAlgorithms)
std::deque<int> deque{1, 2};
std::list<int> list{1, 2, 3};
std::forward_list<int> forward_list{1, 2, 3, 4};
std::array<int, 5> array{1, 2, 3, 4, 5};
const std::array<int, 5> array{1, 2, 3, 4, 5};
std::string string{"123456"};
std::set<int> set{1, 2, 3, 4, 5, 6};
std::multiset<int> multiset{1, 2, 3, 4, 5, 6};
Expand Down Expand Up @@ -57,9 +60,32 @@ TEST(ZipIntegrationTest, ContainersAndAlgorithms)
auto&& [current_l, current_f] = current;
auto&& [next_l, next_f] = next;

std::cout << current_l << ", " << current_f << " vs " << next_l << ", " << next_f << "\n\n";

return current_l + current_f + next_l + next_f == 6;
});
EXPECT_EQ(adjacent_iterator, adjacent_zip.begin());

msd::zip zip2(list, set);
auto iter = zip2.begin();

std::size_t iterations = 0;
while (iter != zip2.end()) { // NOLINT(altera-id-dependent-backward-branch)
auto [a, b] = *iter;
EXPECT_EQ(a, b);
++iterations;
++iter;
}
EXPECT_EQ(iterations, 3);

iterations = 0;
for (auto it = zip2.cbegin(); it != zip2.cend(); ++it) { // NOLINT(altera-id-dependent-backward-branch)
++iterations;
}
EXPECT_EQ(iterations, 3);

iterations = 0;
// NOLINTNEXTLINE(altera-id-dependent-backward-branch)
for (auto it = std::next(zip2.begin()); it != std::prev(zip2.end()); ++it) {
++iterations;
}
EXPECT_EQ(iterations, 1);
}
Loading

0 comments on commit a7c4da5

Please sign in to comment.