Slices in Cilk

#include <cilk/cilk.h>
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    // Unlike arrays, vectors in C++ are dynamically sized.
    // An uninitialized vector has size 0.
    std::vector<std::string> s;
    std::cout << "uninit: " << (s.empty() ? "true" : "false") << " " << (s.size() == 0 ? "true" : "false") << std::endl;

    // To create an empty vector with non-zero size, we can use the constructor.
    s = std::vector<std::string>(3);
    std::cout << "emp: [";
    for (const auto& elem : s) std::cout << elem << " ";
    std::cout << "] len: " << s.size() << " cap: " << s.capacity() << std::endl;

    // We can set and get just like with arrays.
    s[0] = "a";
    s[1] = "b";
    s[2] = "c";
    std::cout << "set: [";
    for (const auto& elem : s) std::cout << elem << " ";
    std::cout << "]" << std::endl;
    std::cout << "get: " << s[2] << std::endl;

    // size() returns the length of the vector as expected.
    std::cout << "len: " << s.size() << std::endl;

    // We can use push_back to append elements to a vector.
    s.push_back("d");
    s.push_back("e");
    s.push_back("f");
    std::cout << "apd: [";
    for (const auto& elem : s) std::cout << elem << " ";
    std::cout << "]" << std::endl;

    // Vectors can be copied using the assignment operator.
    std::vector<std::string> c = s;
    std::cout << "cpy: [";
    for (const auto& elem : c) std::cout << elem << " ";
    std::cout << "]" << std::endl;

    // We can use iterators to get a slice of the vector.
    auto l = std::vector<std::string>(s.begin() + 2, s.begin() + 5);
    std::cout << "sl1: [";
    for (const auto& elem : l) std::cout << elem << " ";
    std::cout << "]" << std::endl;

    // This gets a slice up to (but excluding) the 5th element.
    l = std::vector<std::string>(s.begin(), s.begin() + 5);
    std::cout << "sl2: [";
    for (const auto& elem : l) std::cout << elem << " ";
    std::cout << "]" << std::endl;

    // And this gets a slice from (and including) the 3rd element.
    l = std::vector<std::string>(s.begin() + 2, s.end());
    std::cout << "sl3: [";
    for (const auto& elem : l) std::cout << elem << " ";
    std::cout << "]" << std::endl;

    // We can declare and initialize a vector in a single line as well.
    std::vector<std::string> t = {"g", "h", "i"};
    std::cout << "dcl: [";
    for (const auto& elem : t) std::cout << elem << " ";
    std::cout << "]" << std::endl;

    // The C++ standard library contains a number of useful utility functions for vectors.
    std::vector<std::string> t2 = {"g", "h", "i"};
    if (t == t2) {
        std::cout << "t == t2" << std::endl;
    }

    // Vectors can be composed into multi-dimensional data structures.
    std::vector<std::vector<int>> twoD(3);
    for (int i = 0; i < 3; i++) {
        int innerLen = i + 1;
        twoD[i] = std::vector<int>(innerLen);
        for (int j = 0; j < innerLen; j++) {
            twoD[i][j] = i + j;
        }
    }
    std::cout << "2d: [";
    for (const auto& row : twoD) {
        std::cout << "[";
        for (const auto& elem : row) std::cout << elem << " ";
        std::cout << "] ";
    }
    std::cout << "]" << std::endl;

    return 0;
}

This Cilk code demonstrates the equivalent concepts of slices in C++ using vectors. Here are some key points:

  1. We use std::vector as the equivalent of slices in Go. Vectors in C++ are dynamically sized containers.

  2. Instead of append, we use the push_back method to add elements to the end of a vector.

  3. Slicing in C++ is typically done using iterators. We create new vectors from ranges of existing vectors to demonstrate slicing.

  4. The cilk::cilk header is included for Cilk-specific features, although this example doesn’t explicitly use any Cilk parallelism.

  5. We use range-based for loops to print vector contents, which is more idiomatic in C++.

  6. The slices.Equal function from Go is replaced with the == operator, which works for vectors of comparable types in C++.

  7. Multi-dimensional vectors are created as vectors of vectors.

This code maintains the structure and explanations of the original Go example while adapting it to C++ and Cilk conventions.