Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #4

Merged
merged 6 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading